题目链接:poj 2387 Til the Cows Come Home Please click her
这道题本身可以说并不难,我想借助这道题将我所掌握的四种万能求最短路的方法一一列举出来,如果你能收获一点,那么博主我是很开心的,我是尽自己的努力写了。希望读者能体谅一下博主的不容易!在此深表感谢!
【1】dijkstra算法 点这里(详解)!
【2】flyod算法 点这里(详解)!
【3】spfa算法 详解传送门 !
【4】Bellman-Ford算法 (判断是否存在负权回路)
【5】链式前向星点这里(通俗的介绍)
推荐下我队友zcl的关于这块知识点的博客讲解(他讲的挺好的,希望读者能有所收获):https://blog.csdn.net/hpu2022/article/details/81674112
提供四种解法(希望许多像我一样努力的人能少走弯路)
(1)dijk算法:
//dij算法
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3+10;
int G[maxn][maxn];
int dis[maxn],vis[maxn];
int main()
{
int m,n,u,v,w;
while(cin>>m>>n)
{
memset(vis,0,sizeof(vis));
//memset(dis,inf,sizeof(dis));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) G[i][j]=0;
else G[i][j]=inf;
}
}
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
G[u][v]=G[v][u]=w<G[u][v]?w:G[u][v]; //卡重边(挑最小的)
}
for(int i=1;i<=n;i++)
dis[i]=G[i][1];
vis[1]=1;
int min1,temp;
for(int i=1;i<n;i++)
{
min1 = inf;
for(int j=1;j<=n;j++) // //查找到原集合的最短的边
{
if(!vis[j] && dis[j] < min1)
{
min1 = dis[j];
temp = j;
}
}
vis[temp] = 1;
/*每并入一个点都要对原来的边进行修正,保证任意时刻源点到目标点的距离都是最短的*/
for(int j=1;j<=n;j++)
{
if(!vis[j] && dis[j]>dis[temp]+G[temp][j])
dis[j] = dis[temp]+G[temp][j];
}
}
//下面的作用是输出1号顶点到各个点的最短距离
/*for(int i=1;i<=n;i++)
printf("%d%c",dis[i],i==n?'\n':' ');*/
cout<<dis[n]<<endl;
}
return 0;
}
(2)【超时代码】flyod算法(这道题肯定是不行的,因为n比较的大,会超时,但是针对n小点还是可以的)
//flyod算法
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e3+10;
int G[maxn][maxn];
int m,n,u,v,w;
int main()
{
while(cin>>m>>n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j) G[i][j]=0;
else G[i][j]=inf;
}
}
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
G[u][v]=G[v][u]=w<G[u][v]?w:G[u][v]; //防止卡重边
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(G[i][j] > G[i][k]+G[k][j])
G[i][j] = G[i][k]+G[k][j];
}
}
}
/*int s,e;
cin>>s>>e;
cout<<G[s][e]<<endl;*/
if(G[1][n]==inf)
cout<<"不存在"<<endl;
else
cout<<G[1][n]<<endl;
}
return 0;
}
(3)spfa算法:
先啰嗦一遍链式前向星
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
struct NODE{
int e;
int value;
int next;
}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v,int w)
{
edge[cnt].e = v;
edge[cnt].value = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int main()
{
memset(head,0,sizeof(head));
cnt = 1;
int n,a,b,c;
cin>>n;
while(n--)
{
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c);
}
int start;
cin>>start;
for(int i=head[start];i!=0;i=edge[i].next)
cout<<start<<"-->"<<edge[i].e<<" "<<edge[i].value<<endl;
cout<<endl;
return 0;
}
样例举例:
#include<iostream>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
int vis[maxn]; //标记数组
int dis[maxn];
struct NODE{
int e1;
int value;
int next;
}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v,int w)
{
edge[cnt].e1 = v;
edge[cnt].value = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void spfa(int s,int t)
{
NODE e;
queue<int>q;
q.push(s);
vis[s] = 1; //顶点入队,打上标记
dis[s] = 0;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0; //队头元素出队,此时消除标记
for(int i=head[u];i!=0;i=edge[i].next)
{
e = edge[i];
if(dis[e.e1] > dis[u] + e.value)
{
dis[e.e1] = dis[u] + e.value;
if(! vis[e.e1])
{
q.push(e.e1);
vis[e.e1] = 1;
}
}
}
}
if(dis[t] == inf)
cout<<"恒哥,你出的题有错误啊!"<<endl;
else
cout<<dis[t]<<endl;
}
int main()
{
int m,n,a,b,c;
while(cin>>m>>n)
{
memset(vis,0,sizeof(vis));
memset(dis,inf,sizeof(dis));
memset(head,0,sizeof(head));
cnt = 1;
for(int i=1;i<=m;i++)
{
cin>>a>>b>>c;
add(a,b,c);
add(b,a,c); //双向边,加上这一句
}
spfa(1,n); //从起点到终点
}
return 0;
}
【4】Bellman-Ford算法(对于本题还没有AC,问题审查中)
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
int dis[maxn],u[maxn],v[maxn],w[maxn];
int main()
{
ios::sync_with_stdio(false);
int m,n;
while(cin>>m>>n)
{
memset(u,0,sizeof(u));
memset(v,0,sizeof(v));
memset(w,0,sizeof(w));
memset(dis,0,sizeof(dis));
for(int i=1;i<=m;i++)
cin>>u[i]>>v[i]>>w[i];
for(int i=1;i<=n;i++)
dis[i] = inf;
dis[1] = 0;
//Bellman-Ford算法核心步骤
for(int k=1;k<=n-1;k++)
{
for(int i=1;i<=m;i++)
{
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
}
}
//检测负权回路
int flag = 0;
for(int i=1;i<=m;i++)
{
if(dis[v[i]] > dis[u[i]] +w[i])
{
flag = 1;
break;
}
}
if(flag == 1)
cout<<"这个图有负权回路啊!"<<endl;
//输出所有点距离起始点的最短距离
for(int i=1;i<=n;i++)
{
if(i!=n)
cout<<dis[i]<<" ";
else
cout<<dis[i]<<endl;
}
//cout<<dis[n]<<endl;
}
return 0;
}
下面是所给的样例: