100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
3
这道题可以算的上是很经典的并查集的题目了,刚开始做的时候也没思路啊,各种wa啊,后来参考了大神的思路http://blog.csdn.net/niushuai666/article/details/6981689,也看了好久,才把那个更新域搞懂。
这个题目,最重要的是处理关系域的更新,两个集合合并了之后,合并成一个集合,之间的关系就变了,就要更新他们之间的关系,其他的地方就和普通的并查集一样,这里才是并查集的本质吧。
#include <stdio.h> #define MAXN 10000 struct node { int father; int relation;//定义他们之间的关系 }; node s[MAXN]; int find(int x)//带动态压缩的查找 { int temp; if(x==s[x].father) return x; temp=s[x].father; s[x].father=find(temp); s[x].relation=(s[x].relation+s[temp].relation)%3;//关系域的更新,要找准他们之间的关系 return s[x].father; } int main() { int n,k,d,x,y,i,sum=0; int root1,root2; scanf("%d%d",&n,&k); for(i=0;i<n;i++)//初始化 { s[i].father=i; s[i].relation=0; } for(i=0;i<k;i++) { scanf("%d%d%d",&d,&x,&y); if(x>n||y>n) { sum++; continue; } if(d==2&&x==y) { sum++; continue; } root1=find(x); root2=find(y); if(root1!=root2)//合并操作 { s[root2].father=root1; s[root2].relation=(3+(d-1)+s[x].relation-s[y].relation)%3;//域的更新 } else { if(d==1&&s[x].relation!=s[y].relation) { sum++; continue; } if(d==2&&((3-s[x].relation+s[y].relation)%3!=d-1)) { sum++; continue; } } } printf("%d\n",sum); return 0; }
这是一种做法,看到挑战程序设计竞赛里面的有另一种做法。这里也可以用不带秩的合并(内存可以少一点)。
#include<stdio.h> #include<string.h> #define MAX 10000 int parent[MAX],rank[MAX];//父亲,树的高度 int n,k,cnt; int find(int r) { int p=r; while(p!=parent[p]) p=parent[p]; while(r!=p) {int temp=parent[r];parent[r]=p;r=temp; } return p; } void unionn(int r1,int r2) { r1=find(r1),r2=find(r2); if(rank[r1]<rank[r2]) {parent[r1]=r2; } else { parent[r2]=r1; if(rank[r1]==rank[r2]) rank[r1]++;//因为r2的父节点变成了r1,深度+1 } } bool same(int a,int b) { return find(a)==find(b); //相等表示同一个集合 } int main() { while(~scanf("%d%d",&n,&k)) { cnt=0; for(int i=0;i<=3*n;i++)//这里赋值到3*n,一开始没注意到,然后结果也不对 { parent[i]=i; } memset(rank,0,sizeof(rank)); for(int i=0;i<k;i++) { int d,r1,r2; scanf("%d%d%d",&d,&r1,&r2); if(r1>n||r2>n) { cnt++;continue; } /*以下要做的,就是r1表示A种类,r1+n表示B,r1+2*n表示C ,r2同理。 */ if(d==1)//同类 { if(same(r1,r2+n)||same(r1,r2+2*n) ) /*这里是处理同类的2种情况, 假如r1(A种类)和r2(B种类)是同一个集合,那么这肯定是假话 假如r1(A种类)和r2(C种类)是同一个集合,也是假话 */ cnt++; else {unionn(r1,r2);unionn(r1+n,r2+n);unionn(r1+2*n,r2+2*n); } /*假如是真话,它们就是同类,分别合成一个集合,A与A,B与B,C与C */ } else//x吃y { if(same(r1,r2)||same(r1,r2+2*n) ) /*这里是处理x吃y的2种情况, 假如r1(A种类)和r2(A种类)是同一个集合,不能同类相吃 ,是假话 假如r1(A种类)和r2(C种类)是同一个集合,也是假话 ,只有C种类吃A种类,A种类只能吃B种类。 */ cnt++; else {unionn(r1,r2+n);unionn(r1+n,r2+2*n);unionn(r1+2*n,r2); } /*假如是真话,那么就让x吃y的关系合成一个集合,A种类吃B,B吃C,C吃A */ } } printf("%d\n",cnt); } return 0; }感觉还是太菜了,有些细节还是处理的不好,理解跟自己能够完全做出来还是两码事啊。。。