题解P3366 最小生成树
hicc0305
2018-04-22 15:19:36
## 1.Kruskal
先对所有的边进行排序,取最小的边。把以取的点通过并查集并起来,如果一条边的两个点已经在并查集内,则选这条边会导致树变成图。所以并查集就是用来判断能否加边的。
```cpp
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
struct edge
{
int x,y,z;
}e[200011];
int f[5010];
bool cmp(edge a,edge b)
{
return a.z<b.z;
}
int find(int x)
{
if(f[x]!=x) f[x]=find(f[x]);
return f[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>e[i].x>>e[i].y>>e[i].z;
sort(e+1,e+m+1,cmp);
int cnt=0,sum=0;
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
{
int u=e[i].x,v=e[i].y;
if(find(u)==find(v)) continue;
sum+=e[i].z;
cnt++;
f[find(u)]=find(v);
if(cnt==n-1) break;
}
cout<<sum;
return 0;
}
```
## 2.Prim
prim适用于稠密的图。用dis[i]记录i到最小生成树这个集合的最小距离,从一个点开始扩展,不断的将最近的点加进这个集合。加进去之后再通过这个点去更新其他点的dis。类似于dijstra
```cpp
#include<map>
#include<vector>
#include<list>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,ans=0,num=1;
int cnt=0;
int f[5010],dis[5010],a[5010][5010];
void prime()
{
int l=0,r=1,u=1;
dis[1]=0,f[1]=1;
while(num<n)
{
int minn=0x7fffff;
num++;
for(int i=1;i<=n;i++)
{
if(dis[i]>a[u][i] && !f[i])
{
dis[i]=a[u][i];//更新其他点的距离
}
}
for(int i=1;i<=n;i++)
{
if(!f[i] && dis[i]<minn)
{
minn=dis[i];
u=i;//取距离最小的一个点再次更新
}
}
ans+=minn;
f[u]=1;
}
}
int main()
{
memset(dis,0x7f,sizeof(dis));
memset(a,0x7f,sizeof(a));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(a[x][y]>z) a[x][y]=z,a[y][x]=z;
}
prime();
if(num==n) cout<<ans;
else cout<<"orz";
return 0;
}
```
加上堆优化(也就是优先队列):
此处引用的是一大佬代码,自己懒得打了
```cpp
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int k,n,m,cnt,sum,ai,bi,ci,head[5005],dis[5005],vis[5005];
struct Edge
{
int v,w,next;
}e[400005];
void add(int u,int v,int w)
{
e[++k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k;
}
typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > q;//优先队列!
void prim()
{
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty()&&cnt<n)
{
int d=q.top().first,u=q.top().second;
q.pop();
if(vis[u]) continue;
cnt++;
sum+=d;
vis[u]=1;
for(int i=head[u];i!=-1;i=e[i].next)
if(e[i].w<dis[e[i].v]) dis[e[i].v]=e[i].w,q.push(make_pair(dis[e[i].v],e[i].v));
}
}
int main()
{
memset(dis,0x7f,sizeof(dis));
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&ai,&bi,&ci);
add(ai,bi,ci);
add(bi,ai,ci);
}
prim();
if(cnt==n) printf("%d",sum);
else printf("orz");
}
```