题目链接: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;
}