poj-1417 True Liars 并查集+DP

题目链接

题目:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。

其中比较重要的是,好人总说真话,坏人总说假话。不需要判断矛盾。唯一解

r表示与祖宗结点是否为同一阵营 0为同一阵营 1为不在同一阵营

通过并查集把所以点名分成多个集合 每个集合分成1 0两个阵营

然后DP判断每个集合中的0或者1是好人判断是否有唯一解




#include "stdio.h"
#include "map"
#include "queue"
#include "iostream"
#include "functional"
#include "math.h"
#include "algorithm"
using namespace std;
const int maxn = 1005;
const int mod = 1000000007 ;
const int inf = 1<<30;
typedef __int64 LL;
typedef pair<int,int> pii;
int k,n,m,tot;
int p[maxn],w[maxn];
int mark[maxn],vis[maxn];
int dp[maxn][maxn];
struct Node
{
	int g,b;
}node[maxn];
int find( int x )
{
	if( p[x] == x )	return x;
	int tmp = p[x];
	p[x] = find( p[x] );
	w[x] = ( w[x]+w[tmp] )%2;
	return p[x];
}
void merge( int a,int b,int g )
{
	int x = find( a );
	int y = find( b );
	if( x != y )
	{
		p[x] = y;
		w[x] = ( w[x] + w[a] - w[b] + g+ 4 )%2;
	}
}
void GetDp()
{
	memset( dp,0,sizeof(dp) );
	dp[0][0] = 1;
	for( int i = 1; i < tot; i ++ )
	{
		for( int j = 0; j <= n; j ++ )
		{
			if( dp[i-1][j] != 0 )
			{
				dp[i][j+node[i].b] += dp[i-1][j];
				dp[i][j+node[i].g] += dp[i-1][j];
			}
		}
	}
}
void fun()
{
	int tmp = n;
	memset( vis,0,sizeof(vis) );
	for( int i = tot-1; i > 0; i -- )
	{
		if( dp[i-1][tmp-node[i].b] == 1 && node[i].b && dp[i-1][tmp-node[i].g] == 1 && node[i].g  )
			continue;
		if( dp[i-1][tmp-node[i].b] == 1 && node[i].b )
		{
			vis[i] = 1;
			tmp -= node[i].b;
		}
		else if( dp[i-1][tmp-node[i].g] == 1 && node[i].g )
		{
			vis[i] = 2;
			tmp -= node[i].g;
		}
	}
}
void init()
{
	tot = 1;
	for( int i = 1; i <= n+m; i ++ )
	{
		p[i] = i;	mark[i] = -1;
		w[i] = 0;
	}
}
int main()
{
	#ifndef ONLINE_JUDGE     
	freopen("data.txt","r",stdin);     
	#endif
	char str[10];
	int a,b;
	while( scanf("%d%d%d",&k,&n,&m) != EOF,( k||n||m ) )
	{
		init();
		for( int i = 1; i <= k; i ++ )
		{
			scanf("%d%d%s",&a,&b,str);
			merge( a,b,str[0]=='y'?0:1 );
		}
		for( int i = 1; i <= n+m; i ++ )
		{
			find( i );
		}
		for( int i = 1; i <= n+m; i ++ )
		{
			if( mark[p[i]] == -1 )
			{
				node[tot].b = node[tot].g = 0;
				mark[p[i]] = tot ++;
			}
			w[i] == 1?node[mark[p[i]]].g ++:node[mark[p[i]]].b ++;
		}
		if( n == m )
			puts("no");
		else{
			GetDp();
			if( dp[tot-1][n] == 1 )
			{
				fun();
				for( int i = 1; i <= n+m; i ++ )
				{
					if( vis[mark[p[i]]] == 2 && w[i] == 1 )
						printf("%d\n",i);
					else if(  vis[mark[p[i]]] == 1 && w[i] == 0 )
						printf("%d\n",i);
				}
				puts("end");
			}
			else
				puts("no");
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值