并查集基础题 POJ1611 POJ1703 POJ1988

刚学习了并查集,于是找了几道并查集的基础题来做,感觉掌握的不是很牢固,POJ1182食物链和ZOJ3261星星能量这两题都没能做出来..先放着,以后再做.

 

 

POJ1611 确定SARS病毒的传播圈,并查集的第一题,因为在学习阶段,所以就照着两种写法都写了一遍

1.初始化每个元素为独立集合,用rank来标记每个集合的人数,初始为1,然后每次合并跟新根的rank值,并且都是将较小的节点作为根,最后查看rank[0]就可以了

#include <iostream> #include <stdio.h> using namespace std; struct{ int rank; int parent; }ar[30010]; void init(int n){ for(int i=0;i<n;i++){ ar[i].parent=i; ar[i].rank=1; } } int find(int x){ if(x!=ar[x].parent){ ar[x].parent=find(ar[x].parent); } return ar[x].parent; } void merge(int x,int y){ int px=find(x); int py=find(y); if(px==py)return; if(px<py){ ar[py].parent=px; ar[px].rank+=ar[py].rank; }else{ ar[px].parent=py; ar[py].rank+=ar[px].rank; } } int main(){ while(1){ int n,m,group; scanf("%d%d",&n,&m); if(!n&&!m)break; init(n); for(int i=0;i<m;i++){ scanf("%d",&group); int a,b; scanf("%d",&a); for(int j=1;j<group;j++){ scanf("%d",&b); merge(a,b); } } printf("%d/n",ar[0].rank); } }  

 

2.parent初始化为-1,每次合并的时候将parent值相加,最后parent值的相反数就是这个集合的人数.这种方法就是在回溯缩短路径的时候要注意返回的跟并不是根的parent,而是根的值.

#include <iostream> #include <cstdio> using namespace std; int pa[30010]; void make_set(int n){ for(int i=0;i<n;i++){ pa[i]=-1; } } int find_set(int x){ int p=x; while(pa[p]>0)p=pa[p]; while(x!=p){ int t=pa[x]; pa[x]=p; x=t; } return x; } void union_set(int x,int y){ x=find_set(x); y=find_set(y); if(x!=y){ if(pa[x]<pa[y]){ pa[x]+=pa[y]; pa[y]=x; }else{ pa[y]+=pa[x]; pa[x]=y; } } } int main(){ while(1){ int n,m,k; scanf("%d%d",&n,&m); if(!(n||m))break; make_set(n); while(m-->0){ scanf("%d",&k); int a,b; scanf("%d",&a); k--; while(k-->0){ scanf("%d",&b); union_set(a,b); } } printf("%d/n",0-pa[find_set(0)]); } }  

 

两种方法的时间一样,后一种方法的占用内存稍微少一点

 

 

POJ1703 分两派黑帮,是食物链的简化版,想了一会毫无思路,只能靠度娘了,哎,什么时候才能不再是初学者啊~~

两种方法,都挺好的,都学习一下吧.

 

1.设置一个opp数组,x的敌人为opp[x], 分情况合并集合

#include <iostream> #include <cstdio> using namespace std; int opp[100010]; int parent[100010]; void make_set(int n){ for(int i=1;i<=n;i++){ parent[i]=i; opp[i]=0; } } int find_set(int x){ if(x!=parent[x]){ parent[x]=find_set(parent[x]); } return parent[x]; } void union_set(int x,int y){ int fx=find_set(x); int fy=find_set(y); if(fx==fy)return; parent[fy]=fx; } int main(){ int nCase; scanf("%d",&nCase); while(nCase-->0){ int n,m,a,b; char c; scanf("%d%d",&n,&m); make_set(n); while(m-->0){ getchar();//注意吸收回车 scanf("%c%d%d",&c,&a,&b); if(c=='D'){//分情况讨论 if(opp[a]==0&&opp[b]==0){ opp[a]=b; opp[b]=a; }else if(opp[a]==0){ opp[a]=b; union_set(a,opp[b]); }else if(opp[b]==0){ opp[b]=a; union_set(b,opp[a]); }else{ union_set(a,opp[b]); union_set(b,opp[a]); } }else{ int x=find_set(a); int y=find_set(b); int z=find_set(opp[b]); if(x==y){//a和b在相同的帮派 printf("In the same gang./n"); }else if(x==z){//a和b的对手在同一个集合,说明是不同帮派 printf("In different gangs./n"); }else{ printf("Not sure yet./n"); } } } } return 0; } 

 

2.用rank标记该点和根的关系,相同帮派为0,不同帮派为1 感觉重点就是回溯跟新rank.

#include <iostream> #include <cstdio> using namespace std; int rank[100010]; int parent[100010]; void make_set(int n){ for(int i=0;i<n;i++){ parent[i]=i; rank[i]=0;//0代表和父节点同类,1代表异类 } } int find_set(int x){ int t=parent[x]; if(x!=parent[x]){ parent[x]=find_set(t); rank[x]=(rank[t]+rank[x])%2;//回溯跟新 和父节点相同为0,不同为1 } return parent[x]; } void union_set(int x,int y){ int fx=find_set(x); int fy=find_set(y); if(fx==fy)return; parent[fx]=fy; //if(!rank[x]&&rank[y])rank[fx]=0; //if(rank[x]&&!rank[y])rank[fx]=0; //if(rank[x]&&rank[y])rank[fx]=1; //if(!rank[x]&&!rank[y])rank[fx]=1; rank[fx]=(rank[x]+rank[y]+1)%2;//根据x与其根,y与其根,x与y的关系,推导出fx与fy的关系 } int main(){ int nCase; scanf("%d",&nCase); while(nCase-->0){ int n,m,a,b; char c; scanf("%d%d",&n,&m); make_set(n); while(m-->0){ getchar(); scanf("%c%d%d",&c,&a,&b); if(c=='D'){ union_set(a,b); }else{ int x=find_set(a); int y=find_set(b); if(x==y){ if(rank[a]==rank[b]){ printf("In the same gang./n"); }else printf("In different gangs./n"); }else{ printf("Not sure yet./n"); } } } } } 

 

 

POJ1988 每次移动a和b的所在列,最后查询c下面的方块数

其中below和stack两个参数一个是某个方块下面的方块数 和 某一列的高度,感觉这两个参数十分巧妙,注意在路径压缩时以及合并时要及时跟新,另外查询时也要进行一次跟新.

/* Cube Stacking Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 11813 Accepted: 3946 Case Time Limit: 1000MS Description Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations: moves and counts. * In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y. * In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value. Write a program that can verify the results of the game. Input * Line 1: A single integer, P * Lines 2..P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a 'M' for a move operation or a 'C' for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X. Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself. Output Print the output from each of the count operations in the same order as the input file. Sample Input 6 M 1 6 C 1 M 2 4 M 2 6 C 3 C 4 Sample Output 1 0 2 */ #include <cstdio> using namespace std; int parent[30010]; int stack[30010]; //这一堆的总数量 int below[30010]; //每个方块到它父节点之间的方块数 int n; void make_set(){ for(int i=0;i<30010;i++){ parent[i]=i; below[i]=0; stack[i]=1; } } int find_set(int x){ int temp=parent[x]; if(parent[x]!=x){ parent[x]=find_set(parent[x]); below[x]+=below[temp];//回溯压缩路径,同时要跟新below参数 } return parent[x]; } void union_set(int x,int y){ int px=find_set(x); int py=find_set(y); parent[px]=py;//x移到y上,即x堆并入y堆 below[px]+=stack[py]; //x到根的距离即为y的高度 stack[py]+=stack[px];//堆的总数量跟新 } int main() { scanf("%d",&n); make_set(); for(int i=0;i<n;i++){ char c; int a,b; scanf("/n%c",&c); if(c=='M'){ scanf("%d%d",&a,&b); union_set(a,b); }else{ scanf("%d",&a); find_set(a);//跟新a的参数 printf("%d/n",below[a]); } } return 0; } 

 

 

做了几题,感觉掌握了并查集的方法,但还没有掌握并查集的思想,任重而道远,继续努力吧!!!

 

你可能感兴趣的:(并查集基础题 POJ1611 POJ1703 POJ1988)