您的位置:首页 > 其它

并查集学习入门到熟悉

2016-08-07 15:49 387 查看
我的:

并查集就是把具有相同属性的一些事物归为一类,然后归类之后后就相当于形成了多个联通块,其中要注意的是pre数组记录的是他的上级,然而不一定是他的最上级,判断他是不是最上级的话就是他的pre值是不是等于该数本身,即pre[i] == i,其实有多少个最上级就相当于有多少个连通块。然而想要找到某个数的最上级是哪个数的话就是调用finds函数。finds函数里面有一个状态压缩算法,就是找到他的最上级之后,就把他和他的上几级的pre标为他们的最上级,对之后的操作很有帮助。



这个可以参考:http://blog.csdn.net/dellaserss/article/details/7724401,感觉讲的挺好的。

接下来把并查集的一些写的题贴出来吧,并查集不难的。

UVA10608 Friends

这个就是相当于求连通块了,

我的:题意是要找到最多有多少人在同一个集合里面

#include<bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
int pre[maxn];
int num[maxn];
int n,m;

void Init()//初始化
{
for(int i= 1 ;i <= n;i++)
{
pre[i]=i;
num[i]=1;
}
}
int finds(int x)
{
int r=x;
while(r != pre[r])
{
r=pre[r];
}
int i=x,j;
while(i != r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}

void join(int x,int y)
{
int fx=finds(x),fy=finds(y);
if(fx != fy){
pre[fx]=fy;
num[fy]+=num[fx];
}
}

int main()
{
int Tcase;
scanf("%d",&Tcase);
for(int ii =1;ii <= Tcase ;ii ++)
{
scanf("%d%d",&n,&m);
Init();
for(int i=1; i<= m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
join(x,y);
}
int ans=0;
for(int i=1;i <= n;i++){
if(num[i]>ans)
ans= num[i];
}
printf("%d\n",ans);
}
return 0;
}


L2-010. 排座位

这个题就是就是一个连通块,因为朋友的朋友是朋友,而其他的如果没有给出的话就没有必然的联系,就是看他们是不是在一个朋友圈里面了,

之后的判断的敌人的话,就看有没有出现过就可以了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

const int maxn=100000+10;

int n,m;
int pre[maxn];
void Init()
{
for(int i=1  ;i <= n;i++)
{
pre[i]=i;
}
}

int finds(int x)
{
int r=x;
while(r != pre[r])
{
r=pre[r];
}
int i=x,j;
while(i != r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void join(int x,int y)
{
int fx=finds(x),fy=finds(y);
if(fx != fy)
{
pre[fx]=fy;
}
}

int main()
{
int s;
scanf("%d%d%d",&n,&m,&s);
Init();
int a[maxn][2];
int k=0;
for(int ii=1;ii <= m;ii++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);

if(z==1)//z==1代表的是朋友,就进行合并
join(x,y);
else//代表是敌人,记录下来,之后再进行判断
{
a[k][0]=x;
a[k++][1]=y;
}
}
for(int ii=1;ii <= s;ii++)
{
int x,y;
scanf("%d%d",&x,&y);
int flag1=0,flag2=0;
if(finds(x)==finds(y))
flag1=1;
for(int i=0;i<k;i++)
{
if((a[i][0]==x&&a[i][1]==y) || (a[i][0]==y&&a[i][1]==x))
{flag2=1;break;}
}
if(flag1 && ! flag2)
cout<<"No problem"<<endl;
else if(flag1 && flag2)
cout<<"OK but..."<<endl;
else if(!flag1 && flag2)
cout<<"No way"<<endl;
else if(!flag1 && !flag2)
{
cout<<"OK"<<endl;
}
}
return 0;
}


PAT L2-007. 家庭房产

这个题的预处理有点麻烦,需要把变量的一些值(housenum,area)记录下来,还是有点难度的,表示调了很久,不过到了并查集这块就很简单了。

我的:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;

const int maxn=1000+10;
struct Node
{
int data;
int pre;
int num;
double housenum;
double area;
}nod[maxn*maxn/10],ans[maxn*maxn/10];
void Init()
{
for(int i=0 ;i < 10000;i++)
{
nod[i].pre=i;
nod[i].data=i;
nod[i].num=1;
nod[i].housenum=0;
nod[i].area=0;
}
}

int finds(int x)
{
int r=x;
while(r != nod[r].pre)
{
r=nod[r].pre;
}
int i=x,j;
while(i != r)
{
j=nod[r].pre;
nod[r].pre=r;
i=j;
}
return r;
}
void join(int x,int y)
{
int fx=finds(x),fy=finds(y);
if(fx != fy)
{
if(fx<fy)//小的最为上级,为了最后最小的是最上级
{
nod[fy].pre=fx;
nod[fx].num+=nod[fy].num;
nod[fx].housenum+=nod[fy].housenum;
nod[fx].area+=nod[fy].area;
}
else
{
nod[fx].pre=fy;
nod[fy].num+=nod[fx].num;
nod[fy].housenum+=nod[fx].housenum;
nod[fy].area+=nod[fx].area;
}
}
}
bool comp(struct Node a,struct Node b)//最后结果的比较
{
if(fabs(a.area-b.area)<(1e-7))
return a.data<b.data;
return a.area>b.area;
}
void output(int x)//因为在处理的时候当成了整数,为了使各个数是4位输出,
{
if(x>=1000)
cout<<x<<" ";
else
if(x>=100)
cout<<"0"<<x<<" ";
else if(x>=10)
cout<<"00"<<x<<" ";
else if(x>=0)
cout<<"000"<<x<<" ";
}
int m[maxn][maxn],parents[maxn][maxn],x[maxn];
int main()
{
int n;
scanf("%d",&n);
Init();
bool  flag[maxn*maxn/10];
memset(flag,false,sizeof(flag));
for(int i=0 ;i < n;i++)
{
double y;
double z;
scanf("%d",&x[i]);
flag[x[i]]=true;
scanf("%d%d",&parents[i][0],&parents[i][1]);
//        cout<<parents[i][0]<<" "<<parents[i][1]<<" ";
flag[parents[i][0]]=true;
flag[parents[i][1]]=true;
scanf("%d",&m[i][0]);
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0]<<" "<<"washdgsudfs"<<endl;
for(int j=1;j<=m[i][0];j++)
{
//               cout<<m[i][0]<<endl;
scanf("%d",&m[i][j]);
//               cout<<m[i][j]<<" ";
flag[m[i][j]] = true;
}
scanf("%lf%lf",&y,&z);
nod[x[i]].housenum+=y;
nod[x[i]].area+=z;
}

//    for(int i=0;i<n;i++)
//    {
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0];
//        for(int j=1;j<=m[i][0];j++)
//            cout<<m[i][j]<<" ";
//        cout<<nod[x[i]].housenum<<" "<<nod[x[i]].area<<endl;
//    }

for(int i=0;i<n;i++)
{
if(parents[i][0]!= -1)
join(x[i],parents[i][0]);
if(parents[i][1] != -1)
join(x[i],parents[i][1]);
for(int j=1;j<=m[i][0];j++)
join(x[i],m[i][j]);
}
//    for(int i=0;i<n;i++)
//    {
//        cout<<x[i]<<" "<<parents[i][0]<<" "<<parents[i][1]<<" "<<m[i][0];
//        for(int j=1;j<=m[i][0];j++)
//            cout<<m[i][j]<<" ";
//        cout<<nod[x[i]].housenum<<" "<<nod[x[i]].area<<endl;
//    }

int k=0;

for(int i = 0; i <= 10000;i++)//找到最上级,然后复制到ans之中
{
//        cout<<i<<" "<<flag[i]<<" "<<nod[i].pre<<endl;
if(flag[i] && nod[i].pre == i)
{

ans[k].num=nod[i].num;
ans[k].data=nod[i].data;
ans[k].area=nod[i].area/ans[k].num;
ans[k].housenum=nod[i].housenum/ans[k].num;
k++;
}
}

sort(ans,ans+k,comp);cout<<k<<endl;
for(int i=0;i<k;i++)
{
output(ans[i].data);
cout<<ans[i].num<<" ";
printf("%.3lf %.3lf\n",ans[i].housenum,ans[i].area);
}

return 0;
}


POj1611 Suspect

这个题目的意思是0号是感染源,跟感染源在同一个集合里面的话就是suspect,跟suspect在同一个集合里的话也是suspect,就是要求有多少个suspect。

求与0 在同一个连通块的个数,

我的:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;

const int maxn=100000+10;

int n,m;
int pre[maxn];
int num[maxn];
void Init()
{
for(int i=0;i<n;i++)
{
pre[i]=i;
num[i]=1;
}
}

int finds(int x)
{
int r=x;
while(r != pre[r])
{
r=pre[r];
}
int i=x,j;
while(i != r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void join(int x,int y)
{
int fx=finds(x),fy=finds(y);
if(fx != fy)
{
pre[fx]=fy;
num[fy]+=num[fx];
}
}

int main()
{
while(scanf("%d%d",&n,&m) != EOF && (n || m))
{
int x,y,z;
Init();
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
for(int j=0;j<x-1;j++)
{
scanf("%d",&z);
join(y,z);
}
}
int ans=num[finds(0)];与0在同一个连通块之中,0的最上级是finds(0),这个集合的个数就是num[finds(0)]
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: