简述
对于一个图,最小生成树使所有点连通的总边权最小。
并查集的作用主要是将两个集合的元素合并成一个集合,以及查询两个元素是否在同一集合。所以可以优化kruskal算法中查询两个元素的时间复杂度。
大多选用kruskal算法,模板如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
int x,y,z;
}a[200010];
int n,m,ff,fa[100001],ans;
int cmp(node l,node r)
{
return l.z<r.z;
}
int father(int x)
{
if(fa[x]==x) return x;
else return fa[x]=father(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a[i].x>>a[i].y>>a[i].z;
}
for(int i=1;i<=n;i++) fa[i]=i;
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
int fx=father(a[i].x),fy=father(a[i].y);
if(fx!=fy)
{
fa[fx]=fy;
ans+=a[i].z;
ff++;
}
if(ff==n-1) break;
}
if(ff<n-1) cout<<"orz";
else cout<<ans;
return 0;
}
例题
洛谷P2872 Building Roads
这道题就是裸的最小生成树,只需要将一开始就有的边权值设为0即可。
一开始卡在那个距离公式精度那里,加上两个double就行。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
struct node
{
int u,v;
double w;
}a[1000100];
int n,m,fa[1010];
double ans;
int x[1010],y[1010];
int cmp(node l,node r)
{
return l.w<r.w;
}
int father(int x)
{
if(fa[x]==x) return x;
else return fa[x]=father(fa[x]);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
}
for(int i=1;i<=n;i++) fa[i]=i;
int tot=0;
for(int i=1;i<=n-1;i++)
{
for(int j=i+1;j<=n;j++)
{
a[++tot].u=i;a[tot].v=j;
a[tot].w=sqrt((double)(x[i]-x[j])*(x[i]-x[j])+(double)(y[i]-y[j])*(y[i]-y[j]));
}
}
for(int i=1;i<=m;i++)
{
tot++;
scanf("%d%d",&a[tot].u,&a[tot].v);
a[tot].w=0;
}
sort(a+1,a+tot+1,cmp);
int ff=0;
for(int i=1;i<=tot;i++)
{
int fx=father(a[i].u);
int fy=father(a[i].v);
if(fx!=fy) fa[fx]=fy,ff++,ans+=a[i].w;
if(ff==n-1) break;
}
printf("%.2lf",ans);
return 0;
}
洛谷P4172 水管局长
这道题乍一看很简单,所以我就硬钢了一阵子,最后没做出来,还是写写想法。
如果不是会爆水管的话,那应该没啥事发生。我想到了一种挺暴力的做法,遇到爆水管就记录不再使用并求出新的最小生成树,然后碰到询问就在树上跑SPFA求最短路。
但是实际上正解是LCT。。。
错误的代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct node
{
int x,y,w;
}a[100010];
struct donk
{
int to,next,c;
}e[100010];
int n,m,q,g[1010][1010],fa[1010],mx;
int tot,hd[100010];
int dis[1010],v[1010];
void add(int x,int y,int w)
{
e[++tot]=(donk){y,hd[x],w};
hd[x]=tot;
}
int cmp(node l,node r)
{
return l.w<r.w;
}
int father(int x)
{
if(fa[x]==x) return x;
else return fa[x]=father(fa[x]);
}
void kruskal()
{
sort(a+1,a+m+1,cmp);
int ff=0;
mx=0;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++)
{
if(ff==n-1) break;
int fx=father(a[i].x);
int fy=father(a[i].y);
if(fx!=fy&&g[a[i].x][a[i].y])
{
fa[fx]=fy,ff++,mx=max(mx,a[i].w);
add(a[i].x,a[i].y,a[i].w);
}
}
}
int spfa(int sx)
{
queue<int> q;
memset(v,0,sizeof(v));
for(int i=1;i<=n;i++) dis[i]=1e9;
v[sx]=1;
dis[sx]=0;
q.push(sx);
while(!q.empty())
{
int x=q.front();
q.pop();
v[x]=0;
for(int i=hd[x];i>0;i=e[i].next)
{
int y=e[i].to;
if(dis[y]>max(dis[x],e[i].c))
{
dis[y]=max(dis[x],e[i].c);
if(!v[y]) q.push(y);
v[y]=1;
}
}
}
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
g[a[i].x][a[i].y]=1;
}
kruskal();
while(q--)
{
int k;
scanf("%d",&k);
if(k==1)
{
int x,y;
scanf("%d%d",&x,&y);
spfa(x);
cout<<dis[y]<<endl;
}
else
{
int x,y;
scanf("%d%d",&x,&y);
g[x][y]=0;
memset(hd,0,sizeof(hd));
tot=0;
kruskal();
}
}
return 0;
}