CCF之行車路線(迪傑斯特拉演算法,第二次做,90分)
問題描述
試題編號: | 201712-4 |
試題名稱: | 行車路線 |
時間限制: | 1.0s |
記憶體限制: | 256.0MB |
問題描述: |
問題描述 小明和小芳出去鄉村玩,小明負責開車,小芳來導航。 小芳將可能的道路分為大道和小道。大道比較好走,每走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行描述道路,每行包含四個整數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 ≤ 8,1 ≤ m ≤ 10; 對於另外20%的評測用例,不存在小道; 對於另外20%的評測用例,所有的小道不相交; 對於所有評測用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ a, b ≤ n,t是0或1,c ≤ 105。保證答案不超過106。 |
這道題目是我第二次做了,一開始因為fropen("a.txr","r",stdin)這行程式碼沒有註釋而拿了0分。後來發現問題之後,顯示拿了80分。在仔細閱讀題幹,發現數據型別設定有問題,當我把便令改為long long型別之後,最終拿下了90分。但是今天實在太累了,不打算在鑽研這道題目了。
思路:首先還是套用迪傑斯特拉演算法的模板,寫出基本的程式碼框架。這裡給出的路口數目不超過500個,因此可以直接用二維陣列來構建圖。相比之前的模板不同之處在於,我這裡設定了兩個陣列d_long、d_short,分別儲存每個結點到出發點的大道路徑之和、小道路徑之和。其次,更新點權的時候,需要考慮兩個來源,一個來源就是上一個結點的d_short值,一個來源就是上一個結點的d_long值。在更新過程中要特別注意,會有多條連續的小道相連的情況,嗨喲可能就是大道、小道相互交錯的情況,這也是我為什麼要設定pre陣列來儲存路徑上每個結點的前驅結點的原因。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define MAX 505
#define INF 0x3fffffff
struct Node
{
long long cost;//記錄小道的長度
int type;//記錄小道的型別
};
struct Node G[MAX][MAX];
long long d_short[MAX];
long long d_short_square[MAX];
long long d_long[MAX];
long long d[MAX];
int pre[MAX];
int vis[MAX];
int ans=INF;
long long square(long long x)
{
return x*x;
}
void Djs(int s,int n)
{
//priority_queue<Dist> q;
memset(vis,0,sizeof(vis));
fill(d_long,d_long+MAX,INF);
fill(d_short,d_short+MAX,INF);
fill(d_short_square,d_short_square+MAX,INF);
fill(pre,pre+MAX,0);
fill(d,d+MAX,INF);
d[s]=0;
d_long[s]=0;
d_short[s]=0;
while(1)
{
int u=-1;
long long mini=INF;
for(int i=1;i<=n;i++)
{
if(vis[i]==0&&d[i]<mini)
{
u=i;
mini=d[i];
}
}
if(u==-1) break;
vis[u]=1;
for(int v=1;v<=n;v++)
{
if(G[u][v].cost!=INF&&vis[v]==0)
{
if(G[u][v].type==1)
{
long long d1=INF;
long long d2=INF;
if(d_short[u]!=INF)//如果有小道與源結點相連
{
//需要處理一種特殊情況,就是大道、小道交錯出現的情況
if(pre[u]==0) d1=square(d_short[u]+G[u][v].cost);
else d1=d_long[pre[u]]+square(d_short[u]+G[u][v].cost);//可能出現a通過大道與b相連,b通過小道與c相連
//c通過小道與d相連的情況
}
if(d_long[u]!=INF)//如果有大道與源結點相連
d2=square(G[u][v].cost)+d_long[u];
if(d1<d2)
{
d_short[v]=min(d_short[v],d_short[u]+G[u][v].cost);//更新結點對應的小道點權
d_short_square[v]=min(d1,d_short_square[v]);
}
else
{
d_short[v]=min(d_short[v],G[u][v].cost);//由於上一條道路是大道,所以這裡需要從頭開始考慮小道的點權
d_short_square[v]=min(d2,d_short_square[v]);
pre[v]=u;//記錄當前結點的前驅結點
}
d[v]=min(d_short_square[v],d_long[v]);
}
else//當前道路是大道
{
long long d1=INF;
long long d2=INF;
if(d_long[u]!=INF) d1=d_long[u]+G[u][v].cost;
if(d_short[u]!=INF) d2=d_short_square[u]+G[u][v].cost;
long long mini=min(d1,d2);
d_long[v]=min(d_long[v],mini);
d[v]=min(d_short_square[v],d_long[v]);
}
}
}
}
return;
}
int main()
{
int n,m;
int t,a,b;
long long c;
//freopen("a.txt","r",stdin);
scanf("%d %d",&n,&m);
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
G[i][j].cost=INF;
G[j][i].cost=INF;
}
}
for(int i=0;i<m;i++)
{
scanf("%d%d%d%lld",&t,&a,&b,&c);
if(t==1)//小道
{
G[a][b].cost=c;
G[b][a].cost=c;
G[a][b].type=1;
G[b][a].type=1;
}
else
{
G[a][b].cost=c;
G[b][a].cost=c;
G[a][b].type=0;
G[b][a].type=0;
}
}
Djs(1,n);
printf("%lld\n",d[n]);
return 0;
}