您的位置:首页 > 其它

最短路模版整理

2014-08-20 19:03 357 查看
接着前一篇,整理完最小生成树的模版,接下来整理一下最短路的模版。

所有题目都是根据HDU 1874 写的。

图论中求最短路的算法比较多,有Dijkstra、Bellman-ford、Floyd-Warshall、以及SPFA。主要用到的就是这么几个算法。下面一一介绍。

一、Dijkstra

这个算法的做法就是,每次找到一个距源点距离最近的点,然后扩展这个与这个点相邻的点的距离。 这个算法只能针对非负权图进行求单源最短路,算法复杂度是O(V^2)(不优化),但是可以通过堆优化(我就用优先队列写写吧。。)复杂度可以降到O(V+ElogV)

未优化模版:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
int u,v,next,w;
}e[1005];
int head[205],cnt;
int d[205],vis[205];
int n,m;
void addedge(int u,int v,int w){
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
int Dijkstra(int s,int t){
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[s] = 0;
for(int i=0;i<n;i++){
int k = -1,minn = INF;
for(int j=0;j<n;j++)
if(!vis[j]&&d[j]<minn)
minn = d[j],k = j;
if(k == -1)break;
vis[k] = 1;
for(int j = head[k];j!=-1;j = e[j].next){
int v = e[j].v,w = e[j].w;
if(!vis[v]&& d[v]>d[k]+w)
d[v] = d[k]+w;
}
}
if(d[t] == INF)return -1;
return d[t];
}
int main()
{
while(scanf("%d%d",&n,&m)==2){
int u,v,w;
memset(head,-1,sizeof(head));
cnt = 0;
while(m--){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
scanf("%d%d",&u,&v);
printf("%d\n",Dijkstra(u,v));
}
return 0;
}


对于找最小值的过程,可以用堆优化,为了方便,用优先队列实现。
模版如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int ,int > PII;
struct edge{
int u,v,next,w;
}e[1005];
int head[205],cnt;
int d[205],vis[205];
int n,m;
void addedge(int u,int v,int w){
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
int Dijkstra(int s,int t){
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[s] = 0;
priority_queue <PII,vector<PII>,greater<PII> >q;
q.push(make_pair(d[s],s));
while(!q.empty()){
PII t = q.top();
q.pop();
vis[t.second] = 1;
int k = t.second;
for(int j = head[k];j!=-1;j = e[j].next){
int v = e[j].v,w = e[j].w;
if(!vis[v]&&d[v]>d[k]+w)
d[v] = d[k]+w,q.push(make_pair(d[v],v));
}
}
if(d[t] == INF)return -1;
return d[t];
}
int main()
{
while(scanf("%d%d",&n,&m)==2){
int u,v,w;
memset(head,-1,sizeof(head));
cnt = 0;
while(m--){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
scanf("%d%d",&u,&v);
printf("%d\n",Dijkstra(u,v));
}
return 0;
}


二、Bellman-Ford

这个算法思想特别神奇。将起点的距离设为0,其他点设为INF,对于每一条边都做一次松弛操作。总共进行n-1次这样的操作。这样之后,dis数组里存的就是s到各点的最短距离。其实每次操作都只是更新了前一次更新的点,所以进行了很多不必要的扫描。SPFA正是把这些不必要的操作去掉得到的优化。

Bellman-Ford(普通做法 复杂度O(VE))

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
int u,v,w;
}e[1005];
int n,m,cnt;
int d[205];
int Bellman_ford(int s){
memset(d,0x3f,sizeof(d));
d[s] = 0;
for(int i=1;i<n;i++)
for(int j=1;j<=cnt;j++)
d[e[j].v] = min(d[e[j].v],d[e[j].u]+e[j].w);
for(int i=1;i<=cnt;i++)
if(d[e[i].v]>d[e[i].u]+e[i].w)return 0;
return 1;
}
int main()
{
while(scanf("%d%d",&n,&m)==2){
int u,v,w,st,ed;
cnt = 0;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w),e[++cnt].u = u,e[cnt].v = v,e[cnt].w = w;
e[++cnt].u = v,e[cnt].v = u,e[cnt].w = w;
}
scanf("%d%d",&st,&ed);
int ok = Bellman_ford(st);
if(ok && d[ed]!=INF)printf("%d\n",d[ed]);
else puts("-1");
}
return 0;
}


SPFA(好用,好写,复杂度小)

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
int u,v,w,next;
}e[1005];
int head[205],d[205],inq[205],cnt,n,m;
void addedge(int u,int v,int w){
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt++;
}
int SPFA(int s){
memset(inq,0,sizeof(inq));
memset(d,0x3f,sizeof(d));
d[s] = 0;
queue<int>q;
q.push(s);
while(!q.empty()){
int t = q.front();
q.pop();
inq[t] = 0;
for(int i=head[t];i!=-1;i = e[i].next){
int v = e[i].v,w = e[i].w;
if(d[v]>d[t]+w){
d[v] = d[t]+w;
if(!inq[v])inq[v] = 1,q.push(v);
}
}
}
for(int i=0;i<cnt;i++)
if(d[e[i].v]>d[e[i].u]+e[i].w)return 0;
return 1;
}
int main()
{
while(scanf("%d%d",&n,&m)==2){
int u,v,w,st,ed;
cnt = 0;
memset(head,-1,sizeof(head));
while(m--){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w),addedge(v,u,w);
}
scanf("%d%d",&st,&ed);
int ok = SPFA(st);
if(ok&&d[ed]!=INF)printf("%d\n",d[ed]);
else puts("-1");
}
return 0;
}


三、Floyd-Warshall

这个算法非常好写,原理基于动态规划,就不细说了。。能求出任意两点间的最短路,必须使用邻接矩阵存图,复杂度是O(n^3)

模版

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
int g[205][205];
int n,m;
int main()
{
while(scanf("%d%d",&n,&m)==2){
int u,v,w;
memset(g,0x3f,sizeof(g));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);g[u][v] = g[v][u] = min(g[u][v],w);}
int st,ed;
scanf("%d%d",&st,&ed);
for(int i=0;i<n;i++)g[i][i] = 0;
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(g[i][j]>g[i][k]+g[k][j])
g[i][j] = g[i][k]+g[k][j];
if(g[st][ed] == INF)g[st][ed] = -1;
printf("%d\n",g[st][ed]);
}
return 0;
}


算法很多,但是要把这些算法都学以致用很不容易。大概以后每道题都要想想多解,才能把这些图论算法的适用范围和自己的优势给弄清楚吧。还差了很多火候。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: