CCF 201712-4(最短路徑FLoyd+SPFA)
阿新 • • 發佈:2019-01-03
題目:
問題描述 小明和小芳出去鄉村玩,小明負責開車,小芳來導航。小芳將可能的道路分為大道和小道。大道比較好走,每走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行描述道路,每行包含四個整數t, a, b, c,表示一條型別為t,連線a與b兩個路口,長度為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
對於另外20%的評測用例,不存在小道;
對於另外20%的評測用例,所有的小道不相交;
對於所有評測用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是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;
}