[poj1182]食物链 & [poj2912]Rochambeau (边带权和扩展域并查集模型)

版权声明:本文为博主原创文章,转载请附上原博客链接。 https://blog.csdn.net/CABI_ZGX/article/details/82774696

传送门【poj1182】
传送门【poj2912】


这两题之所以可以放在一起讲,一是他们的所求内容高度相似,其次是他们都属于边带权或者拓展域的并查集的基本模型。
这两题的核心思路就是维护三个集合A,B,C,使得他们里面的元素满足A吃B,B吃C,C吃A。 那么这样的具有传递性的关系以及维护他们的连通情况考虑并查集求解。

边带权解法:

先来说边带权的解法,所谓边带权就是并查集的边上带有权值表示,同时在findfa的过程中维护他,一般要写路径压缩。
用一个d数组表示元素和父亲的这条边上的权值,对于元素x和他的父亲fa[x],我们定义:
1、d[x]=0 表示x和fa[x]属于一类,谁都不吃谁
2、d[x]=1 表示x吃fa[x]
3、d[x]=2 表示x被fa[x]吃
现在需要维护d数组,我们分别在findfa(查询父亲)中和在check(判断关系是否合法,也就是这里面要求出两点间关系)中两种情况讨论:
1、findfa(这里的findfa是路径压缩优化的)
我们先把findfa的子模型拿出来
我们要路径压缩的话,我们最后把x提到root的孩子,那么我们就需要知道x和root的关系,所以我们在跳上去的时候要不断地更新d[x](类似于求个关系前缀和)直到root得到x与root的关系。那么每一次的更新可以表示成这样:
在这里插入图片描述
这时候d[x](我和父亲关系)和d[fa[x]](父亲与祖父关系)是已知的,我们现在要求的就是c(我与祖父关系)。
那么看一下d[x],d[fa[x]]分别等于0,1,2的三种情况这里只举几个例子
1、x吃fa(d[x]=1),fa吃gf(d[fa[x]]=1),那么x被gf吃(c=2)
2、x吃fa(d[x]=1),fa和gf一类(d[fa[x]]=0),那么x吃gf(c=1)
3、x吃fa(d[x]=1),fa被gf吃(d[fa[x]]=2),那么x和gf一类(c=1)
……
多列几种你就发现,c=(d[x]+d[fa[x]])%3
那么我们就可以在findfa回溯的过程中,不断地更新d[x]了。

2、check时
设我们现在要判断的两个元素是x和y,那么如果他们在一个并查集我们要看他们关系是否合法,如果不在一个并查集,看他们的的关系是否
①如果x和y在一个并查集时

同样,拿出基本子模型:
在这里插入图片描述
注意这个图和上面那个图的箭头指向是不同的,在d[y]这条边上刚好相反,那么我们要传递过去,就要把d[y]取反(即关系取反,像我那样取012值就是3-d[y])那么同理可得:
c=( d[x]+(3-d[y]) )%3=(d[x]-d[y])%3(模意义下3被去掉了)

②如果x和y不在一个并查集

那么我们就要把他们合并,我们还是拿出子模型判断
这里我们设fx=findfa(x),fy=findfa(y),当前我们设定x->y的关系是now。且默认fa[fx]=fy(也就是x的并查集并到y去)
在这里插入图片描述
我们现在要维护fx和fy的关系c那么,使用我们之前推x和y的关系的方法可以列出一个等式
now=( d[x]+c+(3-d[y]) )%3
移项+模意义下消掉3得
c=d[y]-d[x]+now

综上,我们就可以得到这个模型维护d的方式。

2、扩展域解法

要做题等会更


然后我们就来说题解,其实上面的讲完食物链也就做完了,那么Rochambeau其实就是比食物链多了一个万能人,那么我们枚举那么万能人,然后将我们当前枚举的这个万能人排除在外后看一下剩下的关系是否会矛盾,如果不矛盾就找到了一个万能人,如果矛盾的话就记录一下找到矛盾的最大步数(也就等于找到万能人的最小步数),这个是否矛盾的过程问题就变成食物链啦。


边带权代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=510;
const int M=2010;
int fa[N],d[N];
int findfa(int x)
{
	if(fa[x]==x) return fa[x];
	else
	{
		int rt=findfa(fa[x]);
		d[x]=(d[x]+d[fa[x]])%3;
		fa[x]=rt;
		return fa[x];
	}
}
bool check(int x,int y,int c)
{
	int fx=findfa(x),fy=findfa(y);
	if(fx==fy)//x和y在一个集合,那么这时候他们俩的父亲都是fx 
	{
		if((d[x]-d[y]+3)%3!=c)
			return false;
	}
	else
	{
		fa[fx]=fy;
		d[fx]=(d[y]-d[x]+c+3)%3; 
	}
	return true;
}
struct node
{
	int x,y;
	char ch;
}a[M];
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		for(int i=1;i<=m;i++)
		{
			scanf("%d%c%d",&a[i].x,&a[i].ch,&a[i].y);	
		}
		bool bk=false;
		int tot=0,pos=0,step=0;
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++) fa[j]=j,d[j]=0;
			bk=true;
			for(int j=1;j<=m;j++)
			{
				if(a[j].x==i||a[j].y==i)
                    continue;
				int c;
				if(a[j].ch=='>') c=1;
				else if(a[j].ch=='<') c=2;
				else if(a[j].ch=='=') c=0;
				if(!check(a[j].x,a[j].y,c))
				{
					bk=false;
					step=max(step,j);
					break;	
				}
			}
			if(bk)
			{
				tot++;
				pos=i;
			}
		}
		if(tot==0) puts("Impossible");
		else if(tot>1) puts("Can not determine");
		else printf("Player %d can be determined to be the judge after %d lines\n",pos,step);
	}
	return 0;
	
}

猜你喜欢

转载自blog.csdn.net/CABI_ZGX/article/details/82774696