hiho一下 第二十五周(SPFA)
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
万圣节的晚上,小Hi和小Ho在吃过晚饭之后,来到了一个巨大的鬼屋!
鬼屋中一共有N个地点,分别编号为1..N,这N个地点之间互相有一些道路连通,两个地点之间可能有多条道路连通,但是并不存在一条两端都是同一个地点的道路。
不过这个鬼屋虽然很大,但是其中的道路并不算多,所以小Hi还是希望能够知道从入口到出口的最短距离是多少?
提示:Super Programming Festival Algorithm。输入
每个测试点(输入文件)有且仅有一组测试数据。
在一组测试数据中:
第1行为4个整数N、M、S、T,分别表示鬼屋中地点的个数和道路的条数,入口(也是一个地点)的编号,出口(同样也是一个地点)的编号。
接下来的M行,每行描述一条道路:其中的第i行为三个整数u_i, v_i, length_i,表明在编号为u_i的地点和编号为v_i的地点之间有一条长度为length_i的道路。
对于100%的数据,满足N<=10^5,M<=10^6, 1 <= length_i <= 10^3, 1 <= S, T <= N, 且S不等于T。
对于100%的数据,满足小Hi和小Ho总是有办法从入口通过地图上标注出来的道路到达出口。
输出
对于每组测试数据,输出一个整数Ans,表示那么小Hi和小Ho为了走出鬼屋至少要走的路程。
- 样例输入
-
5 10 3 5 1 2 997 2 3 505 3 4 118 4 5 54 3 5 480 3 4 796 5 2 794 2 5 146 5 4 604 2 5 63
- 样例输出
-
172
vector实现的邻接表1 #include <iostream> 2 #include <cstdio> 3 #include <memory.h> 4 #include <string> 5 #include <vector> 6 #include <queue> 7 #include <set> 8 #include <map> 9 #include <algorithm> 10 #include <climits> 11 using namespace std; 12 13 #define MAXN 111111 14 #define MAX 1111111 15 struct node { 16 int x,weight; 17 }; 18 vector< node > mp[MAXN]; 19 int cost[MAX]; //存储到起始点的最近距离 20 int n,m,s,e; 21 22 bool spfa() 23 { 24 queue<node> q; 25 cost[s] = 0; 26 q.push({s,0}); 27 while (!q.empty()) 28 { 29 node now = q.front(); 30 q.pop(); 31 if (cost[now.x] < now.weight) continue; 32 else cost[now.x] = now.weight; 33 for (int i = 0;i < mp[now.x].size();i++) { 34 //如果未访问过或者距离可更新 35 if (cost[mp[now.x][i].x] == -1 || cost[mp[now.x][i].x] > mp[now.x][i].weight + cost[now.x]) { 36 cost[mp[now.x][i].x] = mp[now.x][i].weight + cost[now.x]; 37 q.push({mp[now.x][i].x,cost[mp[now.x][i].x]}); 38 } 39 40 } 41 } 42 return true; 43 } 44 45 int main() 46 { 47 int a,b,w; 48 memset(cost,-1,sizeof(cost)); //权值为-1表示还未访问过 49 cin >> n >> m >> s >> e; 50 for (int i = 0;i < m; ++ i) { 51 cin >> a >> b >> w; 52 mp[a].push_back({b,w}); 53 mp[b].push_back({a,w}); 54 } 55 if (spfa()) 56 cout << cost[e] << endl; 57 return 0; 58 }
边实现的邻接表
1 #include <iostream> 2 #include <queue> 3 using namespace std; 4 5 #define MAXN 1111111 6 #define inf 1<<20 7 struct Edge{ 8 int to; 9 int w; 10 int next; 11 }; 12 int n,m,s,e; 13 Edge edge[MAXN]; 14 int head[MAXN]; 15 bool vis[MAXN]; 16 int dis[MAXN]; 17 //int cnt[MAXN]; 18 int t = 0; 19 20 bool spfa(int s) 21 { 22 queue<int> q; 23 q.push(s); 24 dis[s] = 0; 25 vis[s] = 1; 26 while (!q.empty()) { 27 int now = q.front(); 28 q.pop(); 29 vis[now] = 0; 30 //cnt[now]++ 31 //if(cnt[now] > 0) return false; //如果一条边入队超过n次 则存在负权 32 for (int i = head[now];i != -1; i = edge[i].next) { 33 int v = edge[i].to; 34 if (dis[v] > dis[now] + edge[i].w) { 35 dis[v] = dis[now] + edge[i].w; 36 if (!vis[v]) 37 { 38 vis[v] = 1; 39 q.push(v); 40 } 41 } 42 } 43 } 44 return true; 45 } 46 47 void addedge(int from,int to,int w) 48 { 49 edge[t].to = to; 50 edge[t].w = w; 51 edge[t].next = head[from]; 52 head[from] = t++; 53 } 54 55 void init() 56 { 57 for (int i = 0;i <= MAXN; ++ i) { 58 head[i] = -1; 59 dis[i] = inf; 60 vis[i] = 0; 61 } 62 } 63 int main() 64 { 65 int a,b,c; 66 cin >> n >> m >> s >> e; 67 init(); 68 for (int i = 0;i < m; ++ i) { 69 cin >> a >> b >> c; 70 addedge(a,b,c); 71 addedge(b,a,c); 72 } 73 spfa(s); 74 cout << dis[e] << endl; 75 76 return 0; 77 }
另外附上大牛的SFPA代码
SPFA的两种写法,bfs和dfs,bfs判别负环不稳定,相当于限深度搜索,但是设置得好的话还是没问题的,dfs的话判断负环很快
1 int spfa_bfs(int s) 2 { 3 queue <int> q; 4 memset(d,0x3f,sizeof(d)); 5 d[s]=0; 6 memset(c,0,sizeof(c)); 7 memset(vis,0,sizeof(vis)); 8 9 q.push(s); vis[s]=1; c[s]=1; 10 //顶点入队vis要做标记,另外要统计顶点的入队次数 11 int OK=1; 12 while(!q.empty()) 13 { 14 int x; 15 x=q.front(); q.pop(); vis[x]=0; 16 //队头元素出队,并且消除标记 17 for(int k=f[x]; k!=0; k=nnext[k]) //遍历顶点x的邻接表 18 { 19 int y=v[k]; 20 if( d[x]+w[k] < d[y]) 21 { 22 d[y]=d[x]+w[k]; //松弛 23 if(!vis[y]) //顶点y不在队内 24 { 25 vis[y]=1; //标记 26 c[y]++; //统计次数 27 q.push(y); //入队 28 if(c[y]>NN) //超过入队次数上限,说明有负环 29 return OK=0; 30 } 31 } 32 } 33 } 34 35 return OK; 36 37 }
1 int spfa_dfs(int u) 2 { 3 vis[u]=1; 4 for(int k=f[u]; k!=0; k=e[k].next) 5 { 6 int v=e[k].v,w=e[k].w; 7 if( d[u]+w < d[v] ) 8 { 9 d[v]=d[u]+w; 10 if(!vis[v]) 11 { 12 if(spfa_dfs(v)) 13 return 1; 14 } 15 else 16 return 1; 17 } 18 } 19 vis[u]=0; 20 return 0; 21 }
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 #define INF 1000000 //无穷大 5 #define MAXN 10 6 using namespace std; 7 struct ArcNode { 8 int to; 9 int weight; 10 struct ArcNode *next; 11 }; 12 queue<int> Q; //队列中的结点为顶点序号 13 int n; //顶点个数 14 ArcNode* List[MAXN]; //每个顶点的边链表表头指针 15 int inq[MAXN]; //每个顶点是否在队列中的标志 16 int dist[MAXN]; // 17 int path[MAXN]; // 18 void SPFA( int src ) 19 { 20 int i, u; //u为队列头顶点序号 21 ArcNode* temp; 22 for( i=0; i<n; i++ ) //初始化 23 { 24 dist[i] = INF; 25 path[i] = src; 26 inq[i] = 0; 27 } 28 dist[src] = 0; 29 path[src] = src; 30 inq[src]++; 31 Q.push( src ); 32 while( !Q.empty() ) { 33 u = Q.front( ); 34 Q.pop( ); 35 inq[u]--; 36 temp = List[u]; 37 while( temp!=NULL ) { 38 int v = temp->to; 39 if( dist[v] > dist[u] + temp->weight ) { 40 dist[v] = dist[u] + temp->weight; 41 path[v] = u; 42 if( !inq[v] ) { 43 Q.push(v); inq[v]++; 44 } 45 } 46 temp = temp->next; 47 } 48 } 49 } 50 int main( ) { 51 int i, j; //循环变量 52 int u, v, w; //边的起点和终点及权值 53 scanf( "%d", &n ); //读入顶点个数 n 54 memset( List, 0, sizeof(List) ); 55 ArcNode* temp; 56 while( 1 ) { 57 scanf( "%d%d%d", &u, &v, &w ); //读入边的起点和终点 58 if( u==-1 && v==-1 && w==-1 ) break; 59 temp = new ArcNode; //构造邻接表 60 temp->to = v; 61 temp->weight = w; 62 temp->next = NULL; 63 if( List[u]==NULL ) 64 List[u] = temp; 65 else{ 66 temp->next = List[u]; 67 List[u] = temp; 68 } 69 } 70 SPFA( 0 ); //求顶点 0到其他顶点的短路径 71 for( j=0; j<n; j++ ) //释放边链表上各边结点所占用的存储空间 72 { 73 temp = List[j]; 74 while( temp!=NULL ) { 75 List[j] = temp->next; 76 delete temp; 77 temp = List[j]; 78 } 79 } 80 int shortest[MAXN]; //输出短路径上的各个顶点时存放各个顶点的序号 81 for( i=1; i<n; i++ ) { 82 printf( "%d\t", dist[i] ); //输出顶点 0到顶点 i的短路径长度 83 //以下代码用于输出顶点 0到顶点 i的短路径 84 memset( shortest, 0, sizeof(shortest) ); 85 int k = 0; //k 表示 shortest 数组中后一个元素的下标 86 shortest[k] = i; 87 while( path[ shortest[k] ] != 0 ) { 88 k++; shortest[k] = path[ shortest[k-1] ]; 89 } 90 k++; 91 shortest[k] = 0; 92 for( j=k; j>0; j-- ) 93 printf( "%d →", shortest[j] ); 94 printf( "%d\n", shortest[0] ); 95 } 96 return 0; 97 } 98 99 100 /*该程序的运行示例如下: 101 输入: 输出: 102 7 103 0 1 6 1 0→3→2→1 104 0 2 5 3 0→3→2 105 0 3 5 5 0→3 106 1 4 -1 0 0→3→2→1→4 107 2 1 -2 4 0→3→5 108 2 4 1 3 0→3→2→1→4→6 109 3 2 -2 110 3 5 -1 111 4 6 3 112 5 6 3 113 -1 -1 -1*/