种类并查集
相关题目sdnu 1076,1078
思路
对于题目中的k个种类,可以开k倍大的数组。
由于对于某一确定元素,我们不清楚它具体属于哪一种类,只知道和另一元素的关系,同类/异类。因此,将每一元素对应到每一种类
假设有三组,五个个体
在本题的分组中n,n+k,n+2*k对应每一种类
{A,B,C,D,E} {A,B,C,D,E} {A,B,C,D,E}
那么对于每次判定同类的操作就是:
输入x,y
1.先判断是否是异类(并查集找祖先)。
2.如果不是,那么就在每一集合中将x->y
if(found(y)==found(x+2*n)||found(x)==found(y+2*n)){
ans++;
continue;
}
link(x,y);
link(x+n,y+n);
link(x+2*n,y+2*n);
异类同样
输入x,y
1.先判断是否同类。
2.如果不是,那么就在不同集合中x->y
if(found(x)==found(y)||found(x)==found(y+2*n)){
ans++;
continue;
}
link(x+2*n,y);
link(x+n,y+2*n);
link(x,y+n);
代码
1076
这里学了一手非递归的找祖先路径优化。。。。
因为递归在路径太长的时候容易爆掉吧,大概!!
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <map>
using namespace std;
#define ll long long
#define maxn 1010
#define maxm 1000005
const int inf=0x3f3f3f3f;
int pre[10005];
int n,m;
int a[1005],b[1005],c[1005];
void init()
{
for(int i=0;i<=n*2;++i)
{
pre[i]=i;
}
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
}
int found(int x)//found函数,查询一个节点的祖先节点;
{
if(pre[x]!=x)//路径优化版
{
pre[x]=found(pre[x]);
}
return pre[x];
}
int foundex(int x) //非递归路径优化
{
int r=x;
while(pre[r]!=r)
{
r=pre[r];
}
int p;
while(x!=r)
{
p=pre[x];
pre[x]=r;
r=p;
}
return r;
}
int unite(int x,int y)
{
int f1,f2;
f1=found(x);
f2=found(y);
if(f1!=f2)
{
pre[f1]=f2;
}
}
bool same(int x,int y)
{
return found(x)==found(y);
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
init();
for(int i=0;i<m;++i)
{
scanf("%d %d %d",&a[i],&b[i],&c[i]);
}
int ans=0;
for(int i=0;i<m;++i)
{
int x,y,z;
x=a[i]-1;y=b[i]-1;z=c[i];
if(x<0||x>=n||y<0||y>=n)
{
ans=1;
break;
}
if(z==1)
{
if(same(x,y)||same(x+n,y+n))
{
ans=1;
break;
}
else
{
unite(x,y+n);
unite(x+n,y);
}
}
else
{
if(same(x,y+n)||same(x+n,y))
{
ans=1;
break;
}
else
{
unite(x,y);
unite(x+n,y+n);
}
}
}
if(ans==0)
printf("NO\n");
else printf("YES\n");
}
return 0;
}
1078
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
#define ll long long
const double pi=acos(-1);
#define MAXN 50007*3
int pre[MAXN];
void init(int n)
{
for(int i=1;i<=3*n;++i)
pre[i]=i;
}
int found(int x)
{
if(x==pre[x]) return x;
pre[x]=found(pre[x]);
return pre[x];
}
void link(int a,int b){
int root1=found(a),root2=found(b);
pre[root1]=root2;
}
int main()
{
int n,k,flag,x,y;
scanf("%d %d",&n,&k);
int ans=0;
init(n);
while(k--)
{
scanf("%d %d %d",&flag,&x,&y);
if(x>n||y>n){
ans++;
continue;
}
if(flag==2&&x==y){
ans++;
continue;
}
if(flag==1){
if(found(y)==found(x+2*n)||found(x)==found(y+2*n)){
ans++;
continue;
}
link(x,y);
link(x+n,y+n);
link(x+2*n,y+2*n);
}
else{
if(found(x)==found(y)||found(x)==found(y+2*n)){
ans++;
continue;
}
link(x+2*n,y);
link(x+n,y+2*n);
link(x,y+n);
}
}
cout<<ans<<endl;
return 0;
}