题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544
题目描述:
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?
Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
Sample Input
2 1
1 2 3
3 3
1 2 5
2 3 5
3 1 2
0 0
Sample Output
3
2
思路:spfa(spfa算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。)
具体思路在下方代码中有说明。
代码如下:
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
#define mem(a, b) memset(a, b, sizeof(a))//这里是将数组a置为0,结合第49行看
using namespace std;
const int maxn = 10010, INF = 0x7fffffff;
int head[maxn], cnt, vis[maxn], d[maxn];//d[]的作用是用来标记结点对应的路径长度
int n, m;
struct node
{
int u, v, w, next;//next的作用是用来寻找上一结点的权值,下方有介绍
}Node[maxn];
void add(int u, int v, int w)
{
Node[cnt].u = u;//cnt是数组下标//结合第83行可知,第一次调用时下标为0
Node[cnt].v = v;
Node[cnt].w = w;
Node[cnt].next = head[u];
head[u] = cnt++;
}
//分析第20 21这两行代码的作用
/*
//下方的u1,u2,u3仅是为了区分第几组的输入值,便于去理解
结合第83行,开始时head内每个值均为-1,所以
第一次调用add函数时:cnt=0.第20行:Node[0].next=head[u1],head[u1]=-1,即Node[0].next=-1;
第21行:head[u1]=0
这里u1的值是下方main函数内第一次输入的起始结点的值
使用之后cnt自增为1
main函数中,i++
第二次调用add函数时:cnt=1.第20行:Node[1].next=head[u1],head[u1]=0
由上方已知,head[u1]已被刷新为0,
这里的0是当cnt=0时赋给head[u1]的值,这里的u1对应的值也是第一次输入的起始点的值;
第21行:head[u2]=1
这里的u2对应的是第二次输入的起始结点的值。
使用之后cnt继续加1,变为2
main函数中,i++
第三次调用add函数时:cnt=2.第20行:Node[2].next=head[u2],head[u2]=2
第21行:head[u3]=2
使用之后cnt=3
*/
void spfa(int s)//起点s
{
queue<int> Q;
for(int i=0; i<=n; i++) d[i] = INF;
d[s] = 0;//起始点到起始点的距离为0
mem(vis, 0);
Q.push(s);//使起始点入队
vis[s] = 1;//这里vis[]的作用不是用来判断是否标记的,而是用来判断是否在队列中,1表示在队列中,0表示不在
while(!Q.empty())
{
int u = Q.front(); Q.pop();
vis[u] = 0;//起始点已出队
for(int i=head[u]; i!=-1; i=Node[i].next)
/*这里for循环内的条件也很特殊
i的初值为最后一组数据对应的起始点,通过不断找上一结点,来结束循环
终止条件为i!=-1,由head[u1]=-1已知,第一组起始点对应的head值为-1,当找到-1时就代表已找到第一组结点值,结束
i的变化条件是,i不断被赋予上一组的值
*/
{
node e = Node[i];//这里e的作用就是Node[maxn]的作用,为了简便换个名称
//d[e.v]就相当于d[Node[i].v],同理适用于d[e.u]与e.w
if(d[e.v] > d[e.u] + e.w)//d[e.v]表示0-v结点的长度,d[e.u]表示0-u结点的长度
//这里是用来刷新最短路径的长度的
{
d[e.v] = d[e.u] + e.w;
if(!vis[e.v])//起始设置均为0,如果不在队列中,就让其入队。入队操作在比较操作之后
{
Q.push(e.v);//终点入队
vis[e.v] = 1;//表示已在队列之中
}
}
}
}
}
int main()
{
while(cin>> n >> m && n+m)
{
mem(head, -1);//刷新
cnt = 0;//刷新
for(int i=1; i<=m; i++)//<m 因为输入的是几条路
{
int u, v, w;
cin>> u >> v >> w;
add(u, v, w);
add(v, u, w);//无向图再加这一步
}
spfa(1);//由题意描述这里从1点开始,到n点结束,故输出的数组对应的是n
cout<< d[n] <<endl;
}
return 0;
}