我的心愿是世界和平!
并查集
O代表修好该台计算机,S代表询问接下来的两台计算机是否可以正常通信。若A和B可以通信,B和C可以通信,则认为A和B也可以通信。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int s[1010];
int x[1010];
int y[1010];
int found(int x)//查找祖先.
{
if(s[x]!=x)
s[x]=found(s[x]);//路径压缩.
return s[x];
}
void change(int x,int y)
{
x=found(x);
y=found(y);
s[x]=y;
}
int main()
{
char c;
int m,n,i,j,p;
double q,d;
cin>>m>>d;//共m台计算机,距离小于d时可以通信.
for(i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
s[i]=0;
}
getchar();
while(cin>>c)
{
if(c=='O')
{
cin>>p;
if(s[p]==0)
s[p]=p;
for(i=1;i<=m;i++)
{
if(s[i]!=0)
{
q=sqrt((y[i]-y[p])*(y[i]-y[p])+(x[i]-x[p])*(x[i]-x[p]));
if(q<=d)
change(p,i);
}
}
}
if(c=='S')
{
cin>>p>>j;
p=found(p);
j=found(j);
if(p==j)
cout<<"SUCCESS"<<endl;
else
cout<<"FAIL"<<endl;
}
}
return 0;
}
题目来源:http://acm.sdibt.edu.cn:8080/vjudge/contest/view.action?cid=1801#problem/E
认为0学生为嫌疑人,所有与其同组的人均认为是嫌疑人。求0所在的集合中的总数。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int s[30010];
int rank[30010];//记录以i为祖先的集合共有多少孩子.
int found(int x)
{
if(s[x]!=x)
s[x]=found(s[x]);
return s[x];
}
void change(int x,int y)
{
x=found(x);
y=found(y);
s[x]=y;
if(x==y);
else
rank[y]+=rank[x];
}
int main()
{
int m,n,i,j,p,q,o,t;
while(cin>>m>>n)
{
if(m==0&&n==0)
break;
for(i=0;i<m;i++)
{
rank[i]=1;
s[i]=i;
}
for(i=1;i<=n;i++)
{
scanf("%d%d",&o,&p);
for(j=1;j<o;j++)
{
scanf("%d",&q);
change(p,q);
}
}
t=0;
while(s[t]!=t)
t=s[t];
cout<<rank[t]<<endl;
}
return 0;
}
题目来源:http://acm.sdibt.edu.cn:8080/vjudge/contest/view.action?cid=1801#problem/F
Prim算法
矩阵模板
#include<stdio.h>
#include<iostream>
using namespace std;
#define max 0x3f3f3f3f;
int map[110][110];//构成一个矩阵,代表村庄间的距离.
int sign[110];//用于标记该村庄是否已经被连接.
int dis[110];//更新到该村庄最小值.
int n,i,j;
void prim()
{
int sum=0,i,j,num,min;
for(i=1;i<=n;i++)
{
map[i][i]=max;//村庄自己不算做最短路径.
sign[i]=0;
dis[i]=map[1][i];//以1村庄为起始点到各村庄间的距离.
}
sign[1]=1;//以1村庄为起始点,该村庄被标记.
for(i=1;i<n;i++)
{
num=max;
min=max;
for(j=1;j<=n;j++)
{
if(sign[j]==0&&dis[j]<=min)//该村庄未被连接且到它的距离最小.
{
num=j;//记录下一个被标记的村庄.
min=dis[j];
}
}
sign[num]=1;
sum+=min;
for(j=1;j<=n;j++)
{
if(sign[j]==0&&dis[j]>map[num][j])//新被标记的村庄到未被标记的村庄有更短路径,更新dis[]数组的值.
dis[j]=map[num][j];
}
}
printf("%d\n",sum);
}
int main()
{
while(cin>>n)//共有n个村庄.
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&map[i][j]);//输入村庄之间的距离.
prim();//利用prim()函数求村庄之间的最小生成树.
}
}
Kruskal算法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int i,n,m,sum;
struct node{
int left;
int right;
int distance;
}edge[5050];//用于记录从left到right的距离.
int s[5050];//并查集将其联系起来.
bool cmp(struct node a,struct node b)
{
return a.distance<b.distance;
}//对所有的距离从小到大排序.
int found(int x)
{
if(s[x]!=x)
s[x]=found(s[x]);
return s[x];
}
void change(int x,int y,int n)
{
int fx=found(x);
int fy=found(y);
if(fx!=fy)//祖先相同证明已连接,不同对其进行连接.
{
s[fx]=fy;
sum+=edge[n].distance;
}
}
int main()
{
while(~scanf("%d%d",&n,&m),n)
{
sum=0;
for(i=1;i<=m;i++)
scanf("%d%d%d",&edge[i].left,&edge[i].right,&edge[i].distance);
for(i=1;i<=m;i++)
s[i]=i;
sort(edge+1,edge+m+1,cmp);//排序
for(i=1;i<=m;i++)
change(edge[i].left,edge[i].right,i);
printf("%d\n",sum);
}
return 0;
}
本文介绍了并查集的基本概念及应用,并通过两个实例详细解析了Prim算法与Kruskal算法在解决最小生成树问题中的实现过程。
268

被折叠的 条评论
为什么被折叠?



