问题 D: 最短路径问题
题目描述
给你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 21 2 5 62 3 4 51 30 0
样例输出
9 11
提示
拿到这题,第一印象就是最短路劲问题(题目写得清清楚楚)。当然,你可以直接套用最短路劲的解法。不过这里我要给大家介绍另一种解法,同样是图论里的算法——深度优先搜索。
从起点开始进行深度优先搜索,当搜索的结点到达终点时查看路径总长度与花费是否比已经得到的最短路径和最小花费小,如果要小的话就使用当前的路径和花费取代最短路径和最小花费。
当遍历到终点时是否就结束了?非也,还需要从其他路径遍历,判断其他路径是否又更短花费更小的。这里就需要用到回溯了。所谓回溯,就是还需要往回走来走其他的路径。那么会不会进入循环状态呢?对遍历过的结点进行标记就能够杜绝循环。当然需要在回溯时取消之前的标记。
解题思路:这是一个最短路的问题,然后权值有两个,一个是路的长度,一个是花费,然后定义以个三维数组就可以了,把长度和花费都存进去进行比较就好了,模板题。
#include <stdio.h>
#include <string.h>
#define N 1010
int book[N];
int e[N][N][2];
int dist[N],cost[N];
int inf=99999999;
int main()
{
int n,m,a,b,d,p,s,t;
int count,i,j,k,min0,min1;
while(scanf("%d%d",&n,&m),n!=0,m!=0)
{
memset(dist,0,sizeof(dist));
memset(cost,0,sizeof(cost));
memset(book,0,sizeof(book));
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j){
e[i][j][0]=0;//距离长度
e[i][j][1]=0;//花费大小
}
else{
e[i][j][0]=inf;
e[i][j][1]=inf;
}
}
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a,&b,&d,&p);
if(d<e[a][b][0])
{
e[a][b][0]=d;
e[a][b][1]=p;
e[b][a][0]=d;
e[b][a][1]=p;
}
else if(d==e[a][b][0]&&p<e[a][b][1])
{
e[a][b][0]=d;
e[a][b][1]=p;
e[b][a][0]=d;
e[b][a][1]=p;
}
}
scanf("%d%d",&s,&t);
for(i=1;i<=n;i++){
dist[i]=e[s][i][0];
cost[i]=e[s][i][1];
}
book[s]=1;
for(count=1;count<=n;count++)
{
min0=inf;
min1=inf;
for(i=1;i<=n;i++)
{
if(book[i]==0){
if(dist[i]<min0){
min0=dist[i];
min1=cost[i];
j=i;
}
else if(dist[i]==min0){
if(cost[i]<min1){
min0=dist[i];
min1=cost[i];
j=i;
}
}
}
}
book[j]=1;
for(k=1;k<=n;k++)
{
if(book[k]==0)
{
if(dist[k]>dist[j]+e[j][k][0])
{
dist[k]=dist[j]+e[j][k][0];
cost[k]=cost[j]+e[j][k][1];
}
else if(dist[k]==dist[j]+e[j][k][0])
{
if(cost[k]>cost[j]+e[j][k][1]){
dist[k]=dist[j]+e[j][k][0];
cost[k]=cost[j]+e[j][k][1];
}
}
}
}
}
printf("%d %d\n",dist[t],cost[t]);
}
return 0;
}