问题 E: 最短路径问题
时间限制: 1 Sec 内存限制: 32 MB
提交: 61 解决: 31
题目描述
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数s,t;起点s,终点t。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
输出
输出 一行有两个数, 最短距离及其花费。
样例输入
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出
9 11
提示
拿到这题,第一印象就是最短路劲问题(题目写得清清楚楚)。当然,你可以直接套用最短路劲的解法。不过这里我要给大家介绍另一种解法,同样是图论里的算法——深度优先搜索。
从起点开始进行深度优先搜索,当搜索的结点到达终点时查看路径总长度与花费是否比已经得到的最短路径和最小花费小,如果要小的话就使用当前的路径和花费取代最短路径和最小花费。
当遍历到终点时是否就结束了?非也,还需要从其他路径遍历,判断其他路径是否又更短花费更小的。这里就需要用到回溯了。所谓回溯,就是还需要往回走来走其他的路径。那么会不会进入循环状态呢?对遍历过的结点进行标记就能够杜绝循环。当然需要在回溯时取消之前的标记。
经验总结
这题木有坑啦~~经典的最短路径,只是有两个度量标尺= =,这里我是用的SPFA算法,感觉这个算法一劳永逸哎,能解决负环问题,而且算法的性能在大部分情况下都优于使用堆优化的迪杰斯特拉算法,各种度量标尺也挺容易加的,嗯!以后就用这个啦!(..•˘_˘•..)
正确代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
bool inq[maxn];
int n,d[maxn],c[maxn],num[maxn];
struct node
{
int v,w,c;
node(int x,int y,int z):v(x),w(y),c(z){};
};
vector<node> adj[maxn];
bool SPFA(int s)
{
fill(d,d+maxn,INF);
memset(inq,0,sizeof(inq));
memset(num,0,sizeof(num));
fill(c,c+maxn,INF);
queue<int> q;
q.push(s);
inq[s]=true;
++num[s];
d[s]=0;
c[s]=0;
while(q.size())
{
int u=q.front();
q.pop();
inq[u]=false;
for(int i=0;i<adj[u].size();++i)
{
int v=adj[u][i].v;
int w=adj[u][i].w;
int t=adj[u][i].c;
if(d[u]+w<d[v])
{
d[v]=d[u]+w;
c[v]=c[u]+t;
if(!inq[v])
{
q.push(v);
inq[v]=true;
++num[v];
if(num[v]>=n)
return false;
}
}
else if(d[u]+w==d[v])
{
if(c[u]+t<c[v])
{
c[v]=c[u]+t;
}
}
}
}
return true;
}
int main()
{
int m,s,t,d1,d2,w,co;
while(~scanf("%d %d",&n,&m))
{
if(n==0)
break;
for(int i=1;i<=n;++i)
adj[i].clear();
for(int i=0;i<m;++i)
{
scanf("%d %d %d %d",&d1,&d2,&w,&co);
adj[d1].push_back(node(d2,w,co));
adj[d2].push_back(node(d1,w,co));
}
scanf("%d %d",&s,&t);
SPFA(s);
printf("%d %d\n",d[t],c[t]);
}
return 0;
}