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

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

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 long
using 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]);
}

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值