MCS 最短路#4 SPFA
来源:互联网 发布:十分钟学会python 编辑:程序博客网 时间:2024/05/21 10:54
题目链接,可TP
------------------------------------------------------------------------------------------------------
先介绍一下 SPFA:
SPFA算法是1994年西安交通大学 段凡丁 提出的。一种求单元最短路的算法。
算法中需要用到的主要变量
int n;//表示n个点,从1到n标号
int s,t;//s为起点,t为终点
int d[N]; //d[i]表示起点s到点i的最短路
int p[N]; //记录路径(或者说记录前驱)
queue<int> q; //一个队列,用STL实现,当然可有手打队列
int vis[N]; // vis[i] = 1表示点i在队列中,vis[i]= 0 表示 不在队列中
------------------------------------------------------------------------------------------------------
几乎所有的最短路算法其步骤都可以分为两步:
1.初始化
d数组全部复制为MAX (无穷大),p数组全部为s (即起点),或者赋值为-1,表示还没有知道前驱,然后d[s] = 0;表示起点不用求最短路径,或者说最短路 就是0,将起点入队;
2.松弛操作
读取队头顶点u,并将队头顶点u出队(消除标记);将与点u相连的虽有点v进行松弛操作,如果更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(标记),如果已经在队列中,不用入队。
以此循环,知道队列为空时,完成了单源最短路的求解。
------------------------------------------------------------------------------------------------------
SPFA可以处理负权边
定理:只要最短路径存在,上述SPFA算法必定能求出最小值。
证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点V的最短路径估计值d[V]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证明完毕)
期望的时间复杂度O(ke),其中k味儿所有顶点进队的平均次数。可以证明k一般小于等于2.
判断有无负环:
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
SPFA的两种写法,bfs 和 dfs ,bfs判别负环不稳定,相当于限深度搜索,但是设置得好的的话还是没没问题的,dfs的话判断负环还是很快的
------------------------------------------------------------------------
最短路#4 SPFA 题解
这题的四种解法大全---->此处TP
#include <iostream>#include <string>#include <vector>#include <set>#include <algorithm>#include <iterator>#include <list>#include <stack>#include <queue>#define MAX 99999999using namespace std;int mp[205][205];int dis[205];int vis[205];int n,m;int st,en;void SPFA(){int i;//初始化路线总和 && 初始化标记for(i=0;i<n;i++){dis[i]=MAX;vis[i]=0;}//创立队列,清空队列queue<int> q;while(!q.empty())q.pop();//起始点标记dis[st]=0;vis[st]=1;q.push(st);while(!q.empty()){int t;t=q.front();q.pop();vis[t]=0;for(i=0;i<n;i++)//找出比之前更短的路径,松弛if(dis[i]>dis[t]+mp[t][i]){dis[i] = dis[t] + mp[t][i];//若没有走过,标记已经走过,并放入队列if( !vis[i]){vis[i]=1;q.push(i);}}}}int main(){int u,v,cost;int i,j;while(cin>>n>>m)//n为点,m为边{//初始化邻接矩阵for(i=0;i<n;i++)for(j=0;j<n;j++)mp[i][j]=MAX;for(i=0;i<m;i++){cin>>u>>v>>cost;if(cost<mp[u][v])mp[u][v]=mp[v][u]=cost;}//输入起始点,和结束点cin>>st>>en;//SPFASPFA();//输出结果if(dis[en] < MAX)cout<<dis[en]<<endl;elsecout<<-1<<endl;}return 0;}
Problem B
POJ 1201 Intervals题目大意:
输入一个整数n,(1<=n<=50.000)n表示有n个区间,接下来n行,每行3个数ai,bi,ci(0<=ai<=bi<=50.000 and 1<= ci<= bi-ai+1)在ai到bi中间 有ci个数要取出,求总共要去除多少数
思路:
百度了一下。此题需用:差分约束+spfa,思路看---->链接
百度大法好呀,百度大法好!
#include <iostream>#include <cstdio>#include <string>#include <queue>#include <cstring>using namespace std;const int M = 151010;const int INF = -9999999;struct Edge //邻接表{ int v; int w; int next; //} edge[M];int dis[M];int head[M]; //保存边的头结点编号bool visit[M];int num,n,maxx,minn;int number ;queue<int> p;int SPFA(){ int e; for(int i = minn; i <= maxx; i++) dis[i] = INF; dis[minn] = 0; p.push(minn);//源点入队 visit[minn] = true; //标记在队中 while( !p.empty() ) { e = p.front(); p.pop(); visit[e] = false; //标记不在队中 for(int i = head[e]; i != -1; i = edge[i].next) { if (dis[edge[i].v] < dis[e] + edge[i].w) //找最长路,三角不等式 { dis[edge[i].v] = dis[e] + edge[i].w; if( !visit[edge[i].v] ) { visit[edge[i].v] = true; p.push(edge[i].v); } } } } return dis[maxx];}int main(){ int a,b,c; num = 0; number = 1; maxx = -9999; minn = 9999; memset(dis,0,sizeof(dis)); memset(head,-1,sizeof(head)); //不要写0啊啊啊啊 memset(visit,false,sizeof(visit)); cin >> n; for(int i = 1; i <= n; i++) { cin >> a >> b >> c; edge[num].next = head[a]; edge[num].v = b + 1; edge[num].w = c; head[a] = num; num ++; if(maxx < b+1) maxx = b+1; if(minn > a) minn = a; } for(int i = minn; i < maxx; i++) { edge[num].next = head[i]; edge[num].v = i+1; edge[num].w = 0; head[i] = num; num++; edge[num].next = head[i+1]; edge[num].v = i; edge[num].w = -1; head[i+1] = num; num++; } int ans = SPFA(); cout << ans <<endl; return 0;}
Problem C
#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#include <iostream>#define INF 0x3f3f3f3f#define maxn 1100using namespace std;struct node {int u, v, w, next;};node edge[1100000];int dist[maxn], head[maxn], cnt;int used[maxn], n, ML, MD;bool vis[maxn];//初始化void init(){cnt = 0;memset(head, -1, sizeof(head));memset(used, 0, sizeof(used));memset(vis, 0 ,sizeof(vis));}void add(int u, int v, int w){edge[cnt].u=u; edge[cnt].v=v;edge[cnt].w=w;edge[cnt].next=head[u];head[u] = cnt++;}//输入ML和MD的两种情况void getmap(){int a, b, d;for(int i = 1; i <= n; ++i)add(i + 1, i, 0);//增加最大距离while(ML--){cin>>a>>b>>d;add(a, b, d);}//减掉最短距离while(MD--){cin>>a>>b>>d;add(b, a, -d);}}void SPFA(){for(int i = 1; i <= n; ++i)dist[i] = INF;dist[1] = 0;queue<int>q;vis[1] = 1;used[1] = 1;q.push(1);while(!q.empty()){int u = q.front();q.pop();if(used[u] > n){printf("-1\n");return ;}vis[u] = 0;for(int i = head[u]; i != -1; i = edge[i].next){int v = edge[i].v;int w = edge[i].w;if(dist[v] > dist[u] + w){dist[v] = dist[u] + w;if(!vis[v]){vis[v] = 1;used[v]++;q.push(v);}}}}if(dist[n] == INF)cout<<"-2"<<endl;else cout<<dist[n]<<endl;return ;}int main (){while(cin>>n>>ML>>MD){init();getmap();SPFA();}return 0;}
Problem D
输入N,N组测试,每一组测试输入P,Q两个数,(1<=P,Q<=1,000,000),最大数字为P,接下来有Q行,每一行a,b,w,在a,b之间的路线权为w,这是个有向图。
思路:
按照上面几道题的思路
百度大法好呀,百度大法好
#include <stdio.h>#include <stdlib.h>#include <string.h>#define nMax 1000010#define eMax 10000010#define inf 1000000010struct EDGE{int v,w,next;}edge[nMax], reEdge[nMax];int nEdge[nMax], nReEdge[nMax];int n, m, queue[eMax];__int64 dis[nMax], sum;bool vis[nMax];void edgeInit(){for (int i = 1; i <= n; ++ i){nEdge[i] = 0;nReEdge[i] = 0;}}void spfaInit(){for (int i = 1; i <= n; ++ i){dis[i] = inf;vis[i] = false;}}void spfa(int * nEdge, EDGE * edge){spfaInit();int head = 0, tail = 1;dis[1] = 0;queue[0] = 1;while (head < tail){int u = queue[head];vis[u] = true;int p = nEdge[u];while (p != 0){int v = edge[p].v, w = edge[p].w;if (dis[v] > dis[u] + w){dis[v] = dis[u] + w;if (!vis[v]){vis[v] = true;queue[tail] = v;tail ++;}}p = edge[p].next;}vis[u] = false;head ++;}for (int i = 1; i <= n; ++ i){sum += dis[i];}}int main(){int t;scanf("%d", &t);while (t --){scanf("%d%d", &n, &m);edgeInit();for (int i = 1; i <= m; ++ i){int u, v, w;scanf("%d%d%d", &u, &v, &w);edge[i].v = v;edge[i].w = w;edge[i].next = nEdge[u];nEdge[u] = i;reEdge[i].v = u;reEdge[i].w = w;reEdge[i].next = nReEdge[v];nReEdge[v] = i;}sum = 0;spfa(nEdge, edge);spfa(nReEdge, reEdge);printf("%I64d\n", sum);}return 0;}
Problem EHEU
最短路径问题 3790
题目大意:
输入两个整数n,m(1<n<=1,000,0<m<100,000),n是点的数量,m是边的数量,n,m为0时结束。可看作无向图,然后输入m行,每行4个数a,b,d,p,表示a和b之间有一条边其长度为d,花费为q,再输入两个数,s和t,分别为起点和终点。输出最短距离(当最短距离有多条路线,输出花费最少的)
思路:
总的思路差不多。然后主要是再多一个花费。
这题不百度不行啊!............
#include <iostream>#include <string>#include <vector>#include <set>#include <algorithm>#include <iterator>#include <list>#include <stack>#include <queue>using namespace std;const int MAX = 1100;const int INF = 0x3fffffff;struct Node{ int len; int money;};Node map[MAX][MAX];Node dis[MAX];bool used[MAX];Node SPFA(int start,int end,int n){ int i;//初始化 for(i=1;i<=n;i++) { dis[i].len = INF; dis[i].money = INF; }//起始位置 dis[start].len = 0; dis[start].money = 0;//建立队列 queue <int> q; q.push(start); used[start] = 1; while(!q.empty()) { int mid; mid = q.front(); q.pop(); used[mid] = 0; for(i=1;i<=n;i++) {//松弛操作 if(map[mid][i].len + dis[mid].len < dis[i].len) { dis[i].len = map[mid][i].len + dis[mid].len; dis[i].money = map[mid][i].money + dis[mid].money; if(!used[i]) { q.push(i); used[i] = 1; } }//当最短距离有多条的时候,输出花费最少的 if(map[mid][i].len + dis[mid].len == dis[i].len && dis[i].money > map[mid][i].money + dis[mid].money) { dis[i].money = map[mid][i].money + dis[mid].money; } } } return dis[end];}int main(){ int n,m; while(cin>>n>>m,n+m) { int i,j;//初始化邻接矩阵 for(i=1;i<MAX;i++) for(j=1;j<MAX;j++) { map[i][j].len = INF; map[i][j].money = INF; } memset(used,0,sizeof(used));//初始化边的长度和花费 for(i=0;i<m;i++) { int a,b,c,d; cin>>a>>b>>c>>d; if(c < map[a][b].len) { map[a][b].len = c; map[b][a].len = c; map[a][b].money = d; map[b][a].money = d; } }//输入起始点,和结束点 int s,e; cin>>s>>e;//SPFA Node x = SPFA(s,e,n);//输出结果 cout<<x.len<<" "<<x.money<<endl; } return 0;}
- MCS 最短路#4 SPFA
- MCS 最短路#2 dijkstra
- 最短路算法(4)- SPFA算法
- SPFA最短路
- 最短路之SPFA
- poj2472最短路spfa
- 最短路SPFA--poj3013
- 最短路 SPFA模板
- 最短路 SPFA
- 最短路spfa
- 最短路SPFA
- 最短路--SPFA算法
- 最短路SPFA算法
- 最短路SPFA
- 最短路SPFA
- 【SPFA】最短路 _HDU2544
- 【最短路】poj2472 SPFA
- 【最短路】poj2387 SPFA
- 关于 RTOS 的选择 (上)
- 机房收费——组合查询的日期和时间的选择实现
- SDNU 1167.花生采摘【NOIP 2004 普及组】【贪心】【8月6】
- Kaggle 机器学习竞赛冠军及优胜者的源代码汇总
- 12170 - Easy Climb(DP+单调队列)
- MCS 最短路#4 SPFA
- Android代码内存优化建议-Android资源篇
- 虚拟机的安装和使用
- android侧滑菜单框架SlidingMenu的使用
- Codeforces Round #178 (Div. 2) B .Shaass and Bookshelf
- 黑马程序员——ios学习笔记 C语言 结构体&链表&枚举
- Java分布式相关
- wpf 获取DataGrid排序后的结果?
- Redis数据库的学习与实践—Redis的常用命令及高级应用