kuangbin 并查集题单 代码

73 篇文章 0 订阅
本文介绍了如何利用并查集解决 POJ 和 Vjudge 上的多项计算机科学问题,包括无线网络连接判断、嫌疑人合并、表格数量计算、答案错误检测、食物链关系等。通过实例展示了带权并查集在处理网络连通性、群体动态、数据结构操作和游戏逻辑等场景中的高效应用。
摘要由CSDN通过智能技术生成

题单链接
https://vjudge.net/article/752

1. POJ-2236 Wireless Network

//https://vjudge.net/problem/POJ-2236
#include <cstdio>
#define N 1005

int f[N];
bool broken[N];
int n,d,D,p,q;
char Q[3];
int X[N],Y[N];

int Fa (int x)
{
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

inline int fail(int u,int v)
{
	return (X[u]-X[v])*(X[u]-X[v])+(Y[u]-Y[v])*(Y[u]-Y[v])>D;
}

int main()
{
	
	scanf("%d%d",&n,&d);
	D=d*d;
	for (int i=1; i<=n; i++) {
		broken[i]=1;
		f[i]=i;
		scanf("%d%d",&X[i],&Y[i]);
	}
	
	while (scanf("%s",Q)!=EOF) {
		if (Q[0]=='O') {
			scanf("%d",&p);
			for (int i=1; i<=n; i++) {
				if (i!=p && !broken[i] && !fail(p,i)) {
					f[Fa(i)]=Fa(p);
				}
			}
			broken[p]=0;
		}
		else {
			scanf("%d%d",&p,&q);
			puts(Fa(p)==Fa(q) ? "SUCCESS" : "FAIL");
		}
	}
	
	return 0;
}

2. POJ-1611 The Suspects

//https://vjudge.net/problem/POJ-1611
#include <cstdio>
#define N 30005

int f[N],vis[N];
int Fa(int x) {return f[x]==x ? x : f[x]=Fa(f[x]);}
int n,m,ans,k,u,v,fu,fv,tmp;

int main()
{
	while (1) {
		scanf("%d%d",&n,&m);
		if (n==0 && m==0) {
			break;
		}
		
		for (int i=0; i<n; i++) {
			f[i]=i;
			vis[i]=0;
		}
		
		while (m--) {
			scanf("%d",&k);
			if (k<=0) {
				continue;
			}
			
			scanf("%d",&v);
			fv=Fa(v);
			while (--k) {
				u=v;
				fu=fv;
				
				scanf("%d",&v);
				fv=Fa(v);
				
				if (fu!=fv) {
					ans--;
					f[fu]=fv;
				}
			}
		}
		
		tmp=Fa(0);
		ans=0;
		for (int i=0; i<n; i++)
			ans+=(Fa(i)==tmp);
		
		printf("%d\n",ans);
		
	}
	return 0;
}

3. HDU-1213 How Many Tables

//https://vjudge.net/problem/HDU-1213
#include <cstdio>
#include <iostream>
using namespace std;

short int f[1000],T,n,m,u,v,fu,fv;
inline int Fa(int x) {
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

int main()
{
	cin >>T;
	while (T--) {
		cin >>n >>m;
		for (int i=0; i<n; i++) {
			f[i]=i;
		}
		while (m--) {
			cin >>u >>v;
			u--,v--;
			fu=Fa(u);
			fv=Fa(v);
			if (fu<fv) {
				swap(fu,fv);
			}
			if (fu!=fv) {
				n--;
				f[fu]=fv;
			}
		}
		cout <<n <<endl;
	}
	return 0;
}

4. HDU-3038 How Many Answers Are Wrong

带权并查集
写了篇博客
https://blog.csdn.net/jackypigpig/article/details/113348863

//https://vjudge.net/problem/HDU-3038
#include <cstdio>

//设当前节点为 x 
//f: 指向的父节点 (f>x) 
//s: 区间 (x,f] 的和 (左开右闭) 
int f[200005],s[200005];
int n,m,a,b,fa,fb,z,ans;

int Fa(int x)
{
	if (f[x]!=x) {
		int tmp=f[x];
		f[x]=Fa(tmp);	//先压缩好父节点的路径,维护好其 s[] 值后才能计算当前节点的 s[] 值 
		s[x]+=s[tmp];
	}
	return f[x];
}

void init() {
	ans=0;
	for (int i=0; i<=n; i++) {	//注意从 0 开始,因为开区间,会用到 0 
		f[i]=i;
		s[i]=0;
	}
}

int main()
{
	while (scanf("%d%d",&n,&m)!=EOF) {
		
		init();
	
		while (m--) {
			scanf("%d%d%d",&a,&b,&z);
			a--;
			fa=Fa(a);
			fb=Fa(b);
			
			if (fa==fb) {
				ans+=(s[a]-s[b]!=z);	//判断是否失败 
			}
			else {
				f[fa]=fb;
				s[fa]=z+s[b]-s[a];	//向量加减得到 
			}
		}
		
		printf("%d\n",ans);
	}
	return 0;
}

5. POJ-1182 食物链

同样,给边附一个取3模意义下的权值,来个带权值并查集即可。

//https://vjudge.net/problem/POJ-1182
#include <cstdio>
#define N 50005

int f[N];	//并查集 
int g[N];	//与父亲的关系 (0:x=f 1:x>f 2:x<f) 
int ans,n,k,D,a,b,fa,fb,p;

int Fa(int x)
{
	if (f[x]!=x) {
		int tmp=f[x];
		f[x]=Fa(tmp);
		g[x]=(g[x]+g[tmp])%3;
	}
	return f[x];
}

void init()
{
	ans=0;
	for (int i=1; i<=n; i++) {
		f[i]=i;
		g[i]=0;
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	init();
	while (k--) {
		scanf("%d%d%d",&D,&a,&b);
		
		if (a>n || b>n) {
			ans++;
			continue;
		}
		
		p=(D==2);	//a->b = p
		
		fa=Fa(a);
		fb=Fa(b);
		if (fa==fb) {
			ans+=((g[a]-g[b]+3)%3!=p);
		}
		else {
			f[fa]=fb;
			g[fa]=(p+g[b]-g[a]+3)%3;
		}
	}
	printf("%d\n",ans);
	return 0;
}

6.

7. POJ-1456 Supermarket

按利润从大到小一次选,能选就选,记下时间,利用并查集压缩路径。

#include <cstdio>
#include <algorithm>
#define I c[i]
using namespace std;
long long ans,n,t,f[10002],p[10002],d[10002],c[10002];

bool cmp(long long x, long long y)
{
	return p[x]>p[y];
}

long long Fa(long long &x)
{
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

int main()
{
	while (scanf("%lld",&n)==1)
	{
		ans=0;
		for (long long i=0; i<=10000; i++)
			f[i]=i;
		for (long long i=1; i<=n; i++)
		{
			scanf("%lld%lld",&p[i],&d[i]);
			c[i]=i;
		}
		sort(c+1,c+n+1,cmp);
		
		for (long long i=1; i<=n; i++)
		{
			t=Fa(d[I]);
			if (t>0)
			{
				ans+=p[I];
				f[t]=t-1;
			}
		}
		
		printf("%lld\n",ans);
		
	}
	return 0;
}

8. POJ-1733 Parity game

带权并查集

#include <cstdio>
#include <map>
using namespace std;
map<int, int> mp;
int L,Q,cnt,l[5005],r[5005],DD[5005],f[10005],v[10005];

int Fa(int x)
{
	if (f[x]==x)
	{
		return x;
	}
	int tmp=f[x];
	f[x]=Fa(tmp);
	v[x]=(v[x]+v[tmp]+2)%2;
	return f[x];
}

int main()
{
	char s[8];
	int fail,a,b,D,fa,fb;
	
	while (scanf("%d",&L)==1)
	{
		scanf("%d",&Q);
		
		mp.clear();
		cnt=0;
		
		fail=Q;
				
		for (int i=1; i<=Q; i++)
		{
			scanf("%d%d%s",&l[i],&r[i],s);			
			DD[i]=(s[0]=='o');
			l[i]--;
			if (!mp[l[i]])
				mp[l[i]]=++cnt;
			if (!mp[r[i]])
				mp[r[i]]=++cnt;
		}
		
		for (int i=1; i<=cnt; i++)
		{
			f[i]=i;
			v[i]=0;
		}
		
		for (int i=1; i<=Q; i++)
		{
			a=mp[l[i]];
			b=mp[r[i]];
			
			fa=Fa(a);
			fb=Fa(b);
			
			D=DD[i];
			
			if (fa==fb)
			{
				if ((v[a]-v[b]+2)%2!=D)
				{
					fail=i-1;
					break;
				}
			}
			else
			{
				v[fa]=(D-v[a]+v[b]+2)%2;
				f[fa]=fb;
			}
		}
		
		printf("%d\n",fail);
		
	}
	return 0;
}

9. POJ-1984 Navigation Nightmare

带权并查集

#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 40005
using namespace std;
long long n,m,aa[N],bb[N],ll[N];
long long c[10005],cnt,k,q[10005][3];
long long a,b,l,fa,fb;
long long fx[N],fy[N],vx[N],vy[N];
char s,ss[N][2];

bool cmp(int x, int y)
{
	return q[x][2]<q[y][2];
}

long long Fa(long long x, long long f[], long long v[])
{
	if (f[x]==x)
	{
		return x;
	}
	long long tmp=f[x];
	f[x]=Fa(tmp,f,v);
	v[x]+=v[tmp];
	return f[x];
}

void solve(long long A, long long B)
{
	long long fxA,fxB,fyA,fyB;
	fxA=Fa(A,fx,vx);
	fxB=Fa(B,fx,vx);
	fyA=Fa(A,fy,vy);
	fyB=Fa(B,fy,vy);
	if (fxA!=fxB || fyA!=fyB)
	{
		puts("-1");
		return;
	}
	printf("%lld\n",abs(vx[A]-vx[B])+abs(vy[A]-vy[B]));
}

int main()
{
	while (scanf("%lld%lld",&n,&m)==2)
	{
		
		for (long long i=1; i<=m; i++)
		{
			scanf("%lld%lld%lld%s",&aa[i],&bb[i],&ll[i],ss[i]);
			if (ss[i][0]=='S' || ss[i][0]=='W')
			{
				ll[i]=-ll[i];
			}
		}
		scanf("%lld",&k);
		for (long long i=1; i<=k; i++)
		{
			scanf("%lld%lld%lld",&q[i][0],&q[i][1],&q[i][2]);
			c[i]=i;
		}
		sort(c+1,c+k+1,cmp);
		
		// init
		for (int i=1; i<=n; i++)
		{
			fx[i]=fy[i]=i;
			vx[i]=vy[i]=0;
		}
		
		cnt=1;
		for (long long i=1; i<=m; i++)
		{
			a=aa[i],b=bb[i],l=ll[i],s=ss[i][0];
			if (s=='E' || s=='W')
			{
				fa=Fa(a,fx,vx);
				fb=Fa(b,fx,vx);
				if (fa!=fb)
				{
					vx[fa]=l-vx[a]+vx[b];
					fx[fa]=fb;
				}
				
				fa=Fa(a,fy,vy);
				fb=Fa(b,fy,vy);
				if (fa!=fb)
				{
					vy[fa]=vy[b]-vy[a];
					fy[fa]=fb;
				}
			}
			else
			{
				fa=Fa(a,fy,vy);
				fb=Fa(b,fy,vy);
				if (fa!=fb)
				{
					vy[fa]=l-vy[a]+vy[b];
					fy[fa]=fb;
				}
				
				fa=Fa(a,fx,vx);
				fb=Fa(b,fx,vx);
				if (fa!=fb)
				{
					vx[fa]=vx[b]-vx[a];
					fx[fa]=fb;
				}
			}
			
			while (cnt<=k && q[cnt][2]==i)
			{
				solve(q[cnt][0],q[cnt][1]);
				cnt++;				
			}
			
			if (cnt>k)
			{
				break;
			}
		}
	}
	return 0;
}

10. POJ-2492 A Bug’s Life

#include <cstdio>
int f[2005],v[2005];

int Fa(int x)
{
	if (f[x]==x)
	{
		return x;
	}
	int tmp=f[x];
	f[x]=Fa(tmp);
	v[x]^=v[tmp];
	return f[x];
}

int main()
{
	int T,n,m;
	scanf("%d",&T);
	for (int t=1; t<=T; t++)
	{
		scanf("%d%d",&n,&m);
		for (int i=1; i<=n; i++)
		{
			f[i]=i;
			v[i]=0;
		}
		int fail=0,a,b,fa,fb;
		for (int i=1; i<=m; i++)
		{
			scanf("%d%d",&a,&b);
			if (fail)
			{
				continue;
			}
			fa=Fa(a);
			fb=Fa(b);
			if (fa==fb)
			{
				if (v[a]^v[b]!=1)
				{
					fail=1;
				}
			}
			else
			{
				v[fa]=1^(v[a]^v[b]);
				f[fa]=fb;
			}
		}
		printf("Scenario #%d:\n",t);
		puts(fail ? "Suspicious bugs found!" : "No suspicious bugs found!");
		if (t<T)
			puts("");
	}
	return 0;
}

11. POJ-2912 Rochambeau

枚举裁判,然后每次都重新维护一个并查集直到出现矛盾,根据矛盾次数与每次的矛盾位点给出答案。

#include <cstdio>
#include <cstring>

#define MAXN 505
#define MAXM 2005

int JUDGE;
int n,m;
int f[MAXN],df[MAXN];

struct round {
	int u,v,d;	//u->v , v-u=d
} rd[MAXM];


void initf()
{
	for (int i=0; i<n; i++) {
		f[i]=i;
		df[i]=0;
	}
}


int Fa(int x)
{
	if (f[x]==x)
		return x;
	int fa=Fa(f[x]);
	df[x]=(df[x]+df[f[x]]+6)%3;
	f[x]=fa;
	return fa;
}


void join(int x, int y, int d)	//y-x=d
{
	int fx=Fa(x);
	int fy=Fa(y);
	if (fx!=fy) {
		f[fy]=fx;
		df[fy]=(d+df[x]-df[y]+6)%3;
	}
	else {
		//这个情况在本程序中不应该会发生 
	}
}


bool check(int &pos)
{
	pos=0;
	int fu,fv;
	for (int i=0; i<m; i++) {
		if (rd[i].u!=JUDGE && rd[i].v!=JUDGE) {
			fu=Fa(rd[i].u);
			fv=Fa(rd[i].v);
			
			if (fu==fv) {
				if ((df[rd[i].v]-df[rd[i].u]+6)%3 != rd[i].d) {
					pos=i+1;
					return false;					
				}
			}
			else {
				join(rd[i].u,rd[i].v,rd[i].d);
			}
		}
	}
	return true;
}


void solve()
{
	int ans;
	int cnt=0;
	int pos=0,tmppos;
	for (int i=0; i<n; i++) {
		JUDGE=i;
		initf();	//初始化并查集 
		if (check(tmppos)) {
			cnt++;
			ans=JUDGE;
		}
		pos=(pos<tmppos) ? tmppos : pos;	//记录按顺序最多需要的条数 
	}
	if (cnt==0) {
		printf("Impossible\n");
	}
	else if (cnt==1) {
		printf("Player %d can be determined to be the judge after %d lines\n",ans,pos);
	}
	else {
		printf("Can not determine\n");
	}
}


int main()
{
	char ch;
	while (scanf("%d%d",&n,&m)==2) {
		for (int i=0; i<m; i++) {
			scanf("%d %c %d",&rd[i].u,&ch,&rd[i].v);
			rd[i].d= (ch=='=') ? 0 : (ch=='>' ? 2 : 1);
		}	
		solve();
	}
	return 0;
}



/*

3 3
0 < 1
1 < 2
2 < 0

3 5
0 < 1
0 > 1
1 < 2
1 > 2
0 < 2

4 4
0 < 1
0 > 1
2 < 3
2 > 3

1 0


*/

12. ZOJ-3261 Connections in Galaxy War

反过来,变成插入边,再维护一个并查集,合并的时候考虑一下s值和编号就行了。ZOJ账号用不了了,没法测,不知道代码对不对。

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

#define MAXN 10005
#define MAXM 20005
#define MAXQ 50005

int s[MAXN];
int n,m,q;
int f[MAXN];


struct qry {
	char opr;
	int x,y;
}Q[MAXQ];


int Fa(int x)
{
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

void conj(pair<int, int> P)
{
	int u=P.first;
	int v=P.second;
	int fu=Fa(u);
	int fv=Fa(v);
	
	if (fu!=fv) {
		if (s[fu]<s[fv]) {
			f[fu]=fv;
		}
		else if (s[fu]>s[fv]) {
			f[fv]=fu;
		}
		else {
			(fu<fv) ? f[fv]=fu : f[fu]=fv;	
		}
	}
}


int main()
{
	
	while (scanf("%d",&n)==1) {
		for (int i=0; i<n; i++) {
			scanf("%d",&s[i]);
			f[i]=i;
		}
		scanf("%d",&m);
		map<pair<int,int>, int> M;
		pair<int, int> E[MAXM];
		for (int i=0; i<m; i++) {
			scanf("%d%d",&E[i].first,&E[i].second);
			if (E[i].first>E[i].second)
				swap(E[i].first,E[i].second);
			M[E[i]]=1;
		}
		
		scanf("%d",&q);
		char str[10];
		for (int i=0; i<q; i++) {
			scanf("%s",str);
			
			if ((Q[i].opr=str[0])=='q') {
				scanf("%d",&Q[i].x);
			}
			else {
				scanf("%d%d",&Q[i].x,&Q[i].y);
				if (Q[i].x>Q[i].y)
					swap(Q[i].x,Q[i].y);
				M[make_pair(Q[i].x,Q[i].y)]=0;
			}
		}
		
		//构造最后状态 
		for (int i=0; i<m; i++) {
			if (M[E[i]]) {
				conj(E[i]);
			}
		}
		
		//倒序处理答案 
		stack<int> ans;
		for (int i=q-1; i>=0; i--) {
			if (Q[i].opr=='q') {
				int tmp=Fa(Q[i].x);
				ans.push(s[tmp]==s[Q[i].x] ? -1 : tmp);
			}
			else {
				conj(make_pair(Q[i].x,Q[i].y));
			}
		}
		
		//顺序输出 
		while (!ans.empty()) {
			printf("%d\n",ans.top());
			ans.pop();
		}
	}
	
	return 0;
}



/*

3
20 20 20
3
1 2
0 1
0 2
4
query 1
query 2
destroy 1 2
query 2
-1
-1
-1


*/

13. HDU-1272 小希的迷宫

模板题,判断是不是一棵树

#include <cstdio>
#include <cstring>

#define N 100005

int f[N];
bool vis[N];

int Fa(int x)
{
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

bool solve()
{
	memset(vis,0,sizeof(vis));
	for (int i=1; i<N; i++)
		f[i]=i;
	
	int u,v,fu,fv,cnt=0,num=0;
	int flag=1;
	while (1) {
		
		scanf("%d%d",&u,&v);
		
		if (u==0 && v==0) {
			puts(flag && (cnt==num-1 || (cnt==0 && num==0)) ? "Yes" : "No");
			return 1;
		}
		else if (u==-1 && v==-1) {
			return 0;
		}
		
		//坑点:注意确认不是那种特殊指令之后才进行点边的计数操作 
		
		cnt++;
		if (!vis[u]) {
			num++;
			vis[u]=true;
		}
		if (!vis[v]) {
			num++;
			vis[v]=true;
		}
		
		fu=Fa(u);
		fv=Fa(v);
		
		if (fu==fv) {
			flag=0;
		}
		else {
			f[fu]=fv;
		}
		
	}
	return 1;
}

int main()
{
	while (solve())
		continue;
	return 0;
}

/*

0 0

1 2

*/

13. POJ-1308 Is It A Tree?

#include <cstdio>
#include <cstring>

#define N 100005

int CNT;
int f[N];
bool vis[N];

int Fa(int x)
{
	return f[x]==x ? x : f[x]=Fa(f[x]);
}

bool solve()
{
	memset(vis,0,sizeof(vis));
	for (int i=1; i<N; i++)
		f[i]=i;
	
	int u,v,fu,fv,cnt=0,num=0;
	int flag=1;
	while (1) {
		
		scanf("%d%d",&u,&v);
		
		if (u==0 && v==0) {
			printf("Case %d ",++CNT);
			puts(flag && (cnt==num-1 || (cnt==0 && num==0)) ? "is a tree." : "is not a tree.");
			return 1;
		}
		else if (u==-1 && v==-1) {
			return 0;
		}
		
		//坑点:注意确认不是那种特殊指令之后才进行点边的计数操作 
		
		cnt++;
		if (!vis[u]) {
			num++;
			vis[u]=true;
		}
		if (!vis[v]) {
			num++;
			vis[v]=true;
		}
		
		fu=Fa(u);
		fv=Fa(v);
		
		if (fu==fv) {
			flag=0;
		}
		else {
			f[fu]=fv;
		}
		
	}
	return 1;
}

int main()
{
	while (solve())
		continue;
	return 0;
}

/*

0 0

1 2

*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值