并查集学习入门到熟悉
来源:互联网 发布:oracle 数据库高可用 编辑:程序博客网 时间:2024/05/01 21:31
我的:
并查集就是把具有相同属性的一些事物归为一类,然后归类之后后就相当于形成了多个联通块,其中要注意的是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;}
- 并查集学习入门到熟悉
- 并查集入门学习
- SpringBoot学习:从入门到熟悉
- EntityFrameWork 从入门到熟悉(2)-简单增删改查
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集入门
- 并查集学习
- 并查集学习
- 并查集学习
- 并查集学习
- 微软C#语言DotNetBar控件的安装和引用问题
- 网络管理常用命令之三 - Netstat 命令详解(图文)
- 算法:从数组中查找重复的元素并分组
- CSS的选择器,优先级
- Android中Service的简单讲解
- 并查集学习入门到熟悉
- 排序算法——快速排序
- Spring MVC简介
- HDU 1285 确定比赛名次(拓扑排序)
- Java进阶(三十九)Java集合类的排序,查找,替换操作
- POJ 1912 A highway and the seven dwarfs(O(log N)求直线与凸包是否相交)
- key-value数据库的一种实现
- linux学习第五篇:安装nginx
- qsort与sort的七种用法!