POJ--1417[True Liars] 并查集+背包

题目大意:

一共有p1+p2个人,分成两组,一组p1,一组p2。给出N个条件,格式如下:

x y yes表示x和y分到同一组

x y no表示x和y分到不同组

问分组情况是否唯一,若唯一则输出方案,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。

 

参考资料:http://hi.baidu.com/buaa_babt/blog/item/ff6c6c40ba89a2136a63e53a.html

 

 

CODE:

/*并查集+背包*/
/*注意判断唯一的时候的DP方案对于求最终的解会有偏差,因为有可能使之前确定下来的方案被新方案覆盖*/
/*AC代码:63ms*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cstring>
#define MAXN 605
using namespace std;
int M,P1,P2,N,scc;
int p[MAXN];//每组集合的根节点 
int rel[MAXN];//每个点与根节点的关系0:相同(yes);1:不同(no)
int num[MAXN][2];//num[i][j]表示某个根节点i下j组的点数
int root[MAXN];
int dp[MAXN][MAXN];//dp[i][j]表示到达说真话有i人,说假话有j人的情况种数
int pre[MAXN][MAXN];//记录路径
bool ans[MAXN];
int find_set(int x)
{
	if(p[x]!=x)
	{
		int t=find_set(p[x]);
		rel[x]^=rel[p[x]];
		p[x]=t;
	}
	return p[x];
}
void Init()
{
	int i,j,u,v,w;
	char s[10];
	N=P1+P2;
	memset(num,0,sizeof(num));
	//初始化
	for(i=1;i<=N;i++)
	{
		p[i]=i;
		rel[i]=0;
	}
	for(i=1;i<=M;i++)
	{  
		scanf("%d%d%s",&u,&v,s);
		w=(s[0]=='n');
		int du=find_set(u);
		int dv=find_set(v);
		if(du!=dv)
		{
			p[du]=dv;//每当根节点改变时他和根节点的关系也要更新
			rel[du]=rel[u]^rel[v]^w;
		}
	}
	//注意点
	for(i=1;i<=N;i++)
		p[i]=find_set(i);
	scc=0;
	for(i=1;i<=N;i++)
	{
		if(p[i]==i)
		{
			scc++;
			root[scc]=i;
			for(j=1;j<=N;j++)
			{if(p[j]==i) ++num[scc][rel[j]];}
		}
	}
}
void Solve()
{
	int i,j,k,x,y;
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(i=1;i<=scc;i++)
	{
		for(j=P1;j>=0;j--)
		for(k=P2;k>=0;k--)
		{
			x=j-num[i][0];y=k-num[i][1];
			if(x>=0&&y>=0&&dp[x][y])
				dp[j][k]+=dp[x][y];
			x=j-num[i][1];y=k-num[i][0];
			if(x>=0&&y>=0&&dp[x][y])
				dp[j][k]+=dp[x][y];
		}
	}
	if(dp[P1][P2]!=1) printf("no\n");
	else
	{
		memset(pre,0,sizeof(pre));
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(i=1;i<=scc;i++)
		{
			for(j=P1;j>=0;j--)
			for(k=P2;k>=0;k--)
			{
				if(dp[j][k]) continue;
				x=j-num[i][0];y=k-num[i][1];
				if(x>=0&&y>=0&&dp[x][y])
				{
					dp[j][k]+=dp[x][y];
					pre[j][k]=0;
					continue;
				}
				x=j-num[i][1];y=k-num[i][0];
				if(x>=0&&y>=0&&dp[x][y])
				{
					dp[j][k]+=dp[x][y];
					pre[j][k]=1;
					continue;
				}
			}
		}
		x=P1;y=P2;
		memset(ans,false,sizeof(ans));
		for(i=scc;i>=1;i--)
		{
			for(j=1;j<=N;j++)
			{if(root[i]==p[j]&&rel[j]==pre[x][y]) ans[j]=true;}
			if(pre[x][y])
			{x-=num[i][1];y-=num[i][0];}
			else
			{x-=num[i][0];y-=num[i][1];}
		}
		for(i=1;i<=N;i++)
		{
			if(ans[i])
				printf("%d\n",i); 
		}
		printf("end\n");
	}
} 
int main()
{
	while(scanf("%d%d%d",&M,&P1,&P2)!=EOF)
	{
		if(M==0&&P1==0&&P2==0) break;
		Init();
		Solve();
	}
	return 0;
}

你可能感兴趣的:(c)