題目鏈接:http://poj.org/problem?id=1724
題意: N個城市,編號1到N。城市間有R條單向道路。 每條道路連接兩個城市,有長度和過路費兩個屬性。 Bob只有K塊錢,他想從城市1走到城市N。問最短共需要走多長的路。如果到不了N ,輸出-1
2<=N<=100
0<=K<=10000
1<=R<=10000 每條路的長度 L, 1 <= L <= 100 每條路的過路費T , 0 <= T <= 100
輸入:
K
N
R
s1 e1 L1 T1
s1 e2 L2 T2
...
sR eR LR TR
s e是路起點和終點
輸出:最短路的長度。
樣例輸入:
5 6 7 1 2 2 3 2 4 3 3 3 4 2 4 1 3 4 1 4 6 2 1 3 5 2 0 5 4 3 2
樣例輸出:
11
解題思路
從城市 1開始深度優先遍歷整個圖,找到所有能到達 N 的走法,選一個最優的。
最優性剪枝:
1) 如果當前已經找到的最優路徑長度爲L ,那麼在繼續搜索的過程中,總長度已經大於等於L的走法,就可以直接放棄,不用走到底了
另一種通用的最優性剪枝思想 ---保存中間計算結果用於最優性剪枝:
2) 如果到達某個狀態A時,發現前面曾經也到達過A,且前面那次到達A所花代價更少,則剪枝。這要求保存到達狀態A的到目前爲止的最少代價。
用midL[k][m] 表示:走到城市k時總過路費爲m的條件下,最優路徑的長度。若在後續的搜索中,再次走到k時,如果總路費恰好爲m,且此時的路徑長度已經超過midL[k][m],則不必再走下去了。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
struct Road
{
int d,L,t;
};
int N,K,R;
vector < vector <Road> > G(110);//用二維數組表示臨界表,G[s]表示和s鄰接的路
int minLen;//全局變量,記錄最短的路徑長度
int totalCost;//當前狀態的花費
int totalLen;//當前狀態的長度
int visited[110];
int minL[110][10010];//minL[i][j]的意義是當到達i點是花費爲j時的最短路徑
void Dfs(int s)
{
if(s==N)
{
minLen=min(minLen,totalLen);
return ;
}
int len=G[s].size();
for(int i=0;i<len;i++)//枚舉和s相鄰接額情況
{
Road r=G[s][i];
if(!visited[r.d])//如果沒有被走過
{
/*下面三個if語句是三條剪枝條件*/
if(totalCost+r.t>K)//如果當前花銷大於K了
continue;
if(totalLen+r.L>=minLen)//如果當前的路徑已經超過了已存在的最短路徑,那就沒必要往後dfs了
continue;
//如果存在兩種方式都到達同一點並且花銷相同,但是如果當前的長度大於另一種方式的長度,則continue
if(totalLen+r.L>minL[r.d][totalCost+r.t])
continue;
minL[r.d][totalCost+r.t]=totalLen+r.L;
visited[r.d]=1;
totalCost+=r.t;
totalLen+=r.L;
Dfs(r.d);
visited[r.d]=0;//因爲可能存在多種方式的dfs的路徑,所以每次dfs之後都要還原到之前的狀態
totalCost-=r.t;
totalLen-=r.L;
}
}
}
int main()
{
cin>>K>>N>>R;
for(int i=0;i<R;i++)
{
int s;
Road r;
cin>>s;
cin>>r.d>>r.L>>r.t;
G[s].push_back(r);
}
totalCost=0;
totalLen=0;
minLen=1<<30;//無窮大
memset(visited,0,sizeof(visited));
for(int i=1;i<=N;i++)
for(int j=0;j<10010;j++)
minL[i][j]=1<<30;
visited[1]=1;
Dfs(1);
if(minLen<(1<<30))
cout<<minLen<<endl;
else
cout<<-1<<endl;
return 0;
}