浅论3大方法求单源最短路以及2大方法求最小生成树

来源:互联网 发布:房屋外观设计简单软件 编辑:程序博客网 时间:2024/05/17 06:10

  • Prim法求最小生成树
  • Kruskal法求最小生成树
  • Dijkstra法求单源最短路
  • Floyed法求单源最短路
  • Bellman_Ford or SPFA 求单源最短路

在打比赛之前要打好基础.这一次我搞了一些单源最短路以及最小生成树的代码.我把它们放在这里.

Prim法求最小生成树

首先我们手上有一个加权连通图,这种我们可以自己脑补.
我建立一个集合E表示已经加入生成树的点.
然后我们随意选择一个点d,让其加入E,并扫描与它相邻的所有点v,找出最小的一条边并将此边所连的v加入E.然后把d更新为v.不断如此做直到E=V(V即我们一开始拿到的图的所有点组成的集合为止).这里用堆进行优化,复杂度O(nlogn).我的代码可能比较骚.大家看看就好.

#include<bits/stdc++.h>#define pii pair<int,int>using namespace std;const int boss=1e5;vector<pii> r[boss+10];bool vis[boss+10];priority_queue<pii,vector<pii>,greater<pii> > q;int main(){int n,m,u,v,c,i,answer=0;for (scanf("%d%d",&n,&m),i=1;i<=m;i++) scanf("%d%d%d",&u,&v,&c),r[u].push_back((pii){c,v}),r[v].push_back((pii){c,u});for (int now=1;--n;answer+=q.top().first,now=q.top().second,q.pop())  {  for (vis[now]=1,i=0;i<r[now].size();i++) if (!vis[r[now][i].second]) q.push(r[now][i]);  while (vis[q.top().second]) q.pop();  }printf("%d",answer);}

Kruskal法求最小生成树

我建议写这个,这个写起来简单很多,不需要过多考虑.
首先对边的权值从小到大排序,然后按顺序选择最小边,只要该边所连接的两点不是已经在生成树上了,就可以添加之.至于判断两点是不是在树上,用并查集就可以了.复杂度也是O(nlogn),但好写多了.因为题目中ans会爆int,我define int long long.

#include<bits/stdc++.h>#define int long longusing namespace std;const int boss=1e5;struct edge{int from,to,cost;}bian[boss*2+10];bool cmp(edge a,edge b){return a.cost<b.cost;}int fa[boss+10],n,m;int find(int x){return x==fa[x]?x:(fa[x]=find(fa[x]));}main(){int i,answer=0;for (scanf("%lld%lld",&n,&m),i=1;i<=m;i++) scanf("%lld%lld%lld",&bian[i].from,&bian[i].to,&bian[i].cost);for (sort(bian+1,bian+m+1,cmp),i=1;i<=n;i++) fa[i]=i;for (int cnt=1,p=1;cnt<n&&p<=m;p++)  {  int u=find(bian[p].from),v=find(bian[p].to);  if (u==v) continue;  fa[u]=v,cnt++,answer+=bian[p].cost;  }printf("%lld",answer);}

Dijkstra法求单源最短路

老师非常毒,要我们输出路径.当然路径什么的不算啥啦,只要用一个pre数组存储到t这个点往回经过的点,然后递归一下就可以了.
至于方法,当然是把dist数组初始化为inf,再定义E为已经选取的点的集合,然后选取开始点d,搜索与其相邻的所有点,将所有点的距离更新成其与d的距离.把d更新为v,然后再次搜索,如果有dist[i]>dist[v]+dist[v][i],则我们就用后者更新前者,以此类推扫过所有点,直到E=V.

#include<bits/stdc++.h>using namespace std;const int inf=0x3f3f3f3f;int s,t,m,n,pre[1010],edge[1010][1010],dist[1010];bool used[1010];void dijkstra(int s){int i,v=0;for (memset(dist,inf,sizeof dist),memset(pre,-1,sizeof pre),dist[s]=0;v!=-1;)  {  for (v=-1,i=1;i<=n;i++) if (!used[i]&&(v==-1||dist[i]<dist[v])) v=i;  if (v!=-1) for (used[v]=true,i=1;i<=n;i++) if (dist[i]>dist[v]+edge[v][i])    {    dist[i]=dist[v]+edge[v][i];    pre[i]=v;    }  }}vector<int> get_lujing(int t){vector<int> lu;for (;t!=-1;t=pre[t]) lu.push_back(t);reverse(lu.begin(),lu.end());return lu;}int main(){int i;memset(edge,inf,sizeof edge);scanf("%d%d%d%d",&s,&t,&n,&m);for (i=1;i<=m;i++)  {  int u,v,c;  scanf("%d%d%d",&u,&v,&c);  edge[u][v]=c,edge[v][u]=c;  }dijkstra(s);printf("%d\n",dist[t]);vector<int> lujing=get_lujing(t);for (i=0;i<lujing.size();i++) printf("%d ",lujing[i]);}

Floyed法求单源最短路

这个代码虽然是O(n^3)的,但是写起来简单,只要一行.核心代码与矩阵乘法相似.
if (edge[i][j]>edge[i][k]+edge[k][j]) edge[i][j]=edge[i][k]+edge[k][j]
Perfect.这里要输出路径,用一个二维数组pre存储,递归输出路径.具体观看代码.

#include<bits/stdc++.h>using namespace std;typedef long long ll;const ll inf=12345678987654321;ll edge[210][210],n,m,pre[210][210];void get_path(ll s,ll t){if (s==t) printf("%lld",t);else  {  printf("%lld ",s);  get_path(pre[t][s],t);  }}int main(){ll s1,t1,s2,t2,i,j,k;scanf("%lld%lld%lld%lld%lld%lld",&s1,&t1,&s2,&t2,&n,&m);memset(edge,63,sizeof edge);memset(pre,-1,sizeof pre);for (i=1;i<=n;i++) pre[i][i]=i;for (i=1;i<=m;i++)  {  ll u,v,c;  scanf("%lld%lld%lld",&u,&v,&c);  edge[v][u]=edge[u][v]=min(edge[u][v],c);  pre[u][v]=u,pre[v][u]=v;  }for (k=1;k<=n;k++) for (i=1;i<=n;i++) for (j=1;j<=n;j++) if (edge[i][j]>edge[i][k]+edge[k][j]) edge[i][j]=edge[i][k]+edge[k][j],pre[i][j]=pre[k][j];else if (edge[i][k]+edge[k][j]==edge[i][j]&&i!=k&&j!=k) pre[i][j]=min(pre[i][j],pre[k][j]);printf("%lld\n",edge[s1][t1]);get_path(s1,t1);printf("\n%lld\n",edge[s2][t2]);get_path(s2,t2);}

Bellman_Ford or SPFA 求单源最短路

这个方法比起上面两个来优点就是可以判负环,如果在更新n-1次之后还能继续更新,就说明此图没有最短路.具体参见代码.

#include<bits/stdc++.h>using namespace std;const int boss=1e5;struct edge{int from,to,cost;}bian[boss*2+10];stack<int> final;int n,m,s,t,pre[boss+10],dist[boss+10];bool nico;void bellman_ford(int s){int i,k;memset(dist,63,sizeof dist);dist[s]=0;for (k=1;k<n;k++)  {  bool judge=0;//这里更好的变量名是update,判断有没有更新过.  for (i=1;i<=m;i++) if (dist[bian[i].to]>dist[bian[i].from]+bian[i].cost)    {    dist[bian[i].to]=dist[bian[i].from]+bian[i].cost;    pre[bian[i].to]=bian[i].from;    judge=1;    }  if (!judge) break;  }for (nico=0,i=1;i<=m;i++) if (dist[bian[i].to]>dist[bian[i].from]+bian[i].cost) nico=1;}vector<int> get_lujing(int t){vector<int> lu;for (;t!=pre[t];t=pre[t]) lu.push_back(t);reverse(lu.begin(),lu.end());return lu;}int main(){int i;scanf("%d%d%d%d",&s,&t,&n,&m);for (i=1;i<=n;i++) pre[i]=i;for (i=1;i<=m;i++) scanf("%d%d%d",&bian[i].from,&bian[i].to,&bian[i].cost);bellman_ford(s);if (nico) return printf("You show me the wrong map!");printf("%d\n",dist[t]);vector<int> lujing=get_lujing(t);for (printf("%d ",s),i=0;i<lujing.size();i++) printf("%d ",lujing[i]);}

好,我就说到这里,谢谢大家的观看.