1. 程式人生 > >CCF 201712-4(最短路徑FLoyd+SPFA)

CCF 201712-4(最短路徑FLoyd+SPFA)

題目:

問題描述  小明和小芳出去鄉村玩,小明負責開車,小芳來導航。
  小芳將可能的道路分為大道和小道。大道比較好走,每走1公里小明會增加1的疲勞度。小道不好走,如果連續走小道,小明的疲勞值會快速增加,連續走s公里小明會增加s2的疲勞度。
  例如:有5個路口,1號路口到2號路口為小道,2號路口到3號路口為小道,3號路口到4號路口為大道,4號路口到5號路口為小道,相鄰路口之間的距離都是2公里。如果小明從1號路口到5號路口,則總疲勞值為(2+2)2+2+22=16+2+4=22。
  現在小芳拿到了地圖,請幫助她規劃一個開車的路線,使得按這個路線開車小明的疲勞度最小。輸入格式  輸入的第一行包含兩個整數n
m,分別表示路口的數量和道路的數量。路口由1至n編號,小明需要開車從1號路口到n號路口。
  接下來m行描述道路,每行包含四個整數tabc,表示一條型別為t,連線ab兩個路口,長度為c公里的雙向道路。其中t為0表示大道,t為1表示小道。保證1號路口和n號路口是連通的。輸出格式  輸出一個整數,表示最優路線下小明的疲勞度。樣例輸入6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1樣例輸出76樣例說明  從1走小道到2,再走小道到3,疲勞度為52=25;然後從3走大道經過4到達5,疲勞度為20+30=50;最後從5走小道到6,疲勞度為1。總共為76。資料規模和約定  對於30%的評測用例,1 ≤ n
 ≤ 8,1 ≤ m ≤ 10;
  對於另外20%的評測用例,不存在小道;
  對於另外20%的評測用例,所有的小道不相交;
  對於所有評測用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ ab ≤ nt是0或1,c≤ 105。保證答案不超過106

思路:因為既可以走大路,又可以走小路,所以把兩種路分開來看,各用一個數組儲存。由於連續走小路時,疲勞值增加連續小路的總長度的平方,所以首先用FLoyd演算法將走小路進行歸併一下,以後在計算的時候就只需要考慮三種情況:1.大路+大路  ,2.小路+大路  ,3.大路+小路。用兩個陣列dis和dis1分別儲存大路和小路到達i點時的最小疲勞值,最後取n處的兩者的最小值

AC程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>

#define inf 0x3f3f3f3f
using namespace std;

int N,M;
int g[501][501];
long long g1[501][501];  //大路和小路 
long long dis[501],dis1[501];
bool vis[501];

void SPFA(int s)
{
	queue<int> Q;
	dis[s] = 0;
	dis1[s]=0;
	Q.push(s);
	vis[s] = 1;
	while (!Q.empty())
	{
		long long tep = Q.front();
		Q.pop();
		vis[tep] = 0;
		for (int i=1;i<=N;i++)
		{
			if(dis[i]>dis[tep]+g[tep][i])  //大路+大路 
			{
				dis[i] = dis[tep] + g[tep][i];
				if (vis[i] == 0)
				{
					Q.push(i);
					vis[i] = 1;
				}
			}
			if(dis[i]>dis1[tep]+g[tep][i])  //小路+大路 
			{
				dis[i] = dis1[tep] + g[tep][i];
				if (vis[i] == 0)
				{
					Q.push(i);
					vis[i] = 1;
				}
			}
			if(g1[tep][i]!=inf)  //可以走小路,由於之前已經把小路進行了歸併,所以只考慮之前走的是大路 
			{
				if(dis1[i]>dis[tep]+g1[tep][i]*g1[tep][i])  
				{
					dis1[i]=dis[tep]+g1[tep][i]*g1[tep][i];
					if (vis[i] == 0)
					{
						Q.push(i);
						vis[i] = 1;
					}
				}
			} 
		}
	}
}

int main()
{
	int t,a,b,c;
	scanf("%d %d", &N, &M);
	memset(vis, 0, sizeof(vis));
	memset(dis, inf, sizeof(dis));
	memset(dis1,inf,sizeof(dis1));
	memset(g,inf,sizeof(g));
	memset(g1,inf,sizeof(g1)); 
	while (M--)
	{
		scanf("%d %d %d %d",&t, &a, &b, &c);
		if(t==1&&c<g1[a][b])
			g1[a][b]=g1[b][a]=c;
		else if(t==0&&c<g[a][b])
			g[a][b]=g[b][a]=c;
	}
	for(int i=1;i<=N;i++) //這裡用floyd事先計算好只走小路時兩兩點之間的最短距離  
    	for(int j=i+1;j<=N;j++)  
    	{  
        	for(int k=1;k<=N;k++)  
        	{  
            	if(k==i||k==j) 
					continue;  
            	if(g1[i][j]>g1[i][k]+g1[k][j]) 
					g1[i][j]=g1[j][i]=g1[i][k]+g1[k][j];  
        	}  
    	}  
	SPFA(1);
	printf("%lld\n", min(dis[N],dis1[N]));
	return 0;
}