POJ 1417 True Liars

解题思路:并查集+dp

分析易知:如果答案为"no",则xi,yi属于两种不同类型(1),否则为同一类型(0)

a)并查集的思想类似于POJ 1733 Parity Game relation[i]表示节点i与根节点关系

b)此时,我们将所有人分成了几个(s)不同的集合,每个集合分为两类:与根节点同类的,与根节点不同类的。

c)题目转化为求解是否存在唯一解:取每个集合中的一种类型(必须且仅取一种),累计人数为p1

  题目转化为动态规划问题,dp[i][j]表示节点i(包括i)之前,人数不超过j的最大人数

  node[i].s表示与该节点同类型的节点个数,node[i].d表示与该节点类型不同的节点个数

 

dp[i][j]=max{dp[i-1][j-node[i].s]+node[i].s, dp[i-1][j-node[i].d]+node[i].d}

  为了保证(必须且仅取一种)条件,必须保证dp公式中 dp[i-1][j-node[i].s],dp[i-1][j-node[i].s](i>1)不为0,否则该项用0代替

 

d)为了保证解唯一,如果dp[i-1][j-node[i].s]==dp[i-1][j-node[i].s],我们可以立即判断不满足条件(该过程仅仅是优化而已,如果不判断,下面过程同样可以判断出解不唯一)

e)如果dp[s][pi]==pi,则说明存在解,为了保证解唯一,(s,pi)到达(0,0)应该只有唯一的路径

 

dp[i][t]-node[i].s == dp[i-1][t-node[i].s]
dp[i][t]
-node[i].d == dp[i-1][t-node[i].d]

  分别表示选取同类与不同类两种情况,我们必须满足二者仅有一个等式满足

欢迎review
#include <iostream>
using namespace std;
#define MAX 601
int root[MAX],same[MAX],diff[MAX],_map[MAX];
int dp[MAX][MAX];
char relation[MAX];
struct{
int s, d, c;
}node[MAX];

int findRoot(int x)
{
int t;
if(x!=root[x]){t = root[x];root[x]=findRoot(root[x]);relation[x]^=relation[t];}
return root[x];
}

int main()
{
int i,j, n, p1, p2, a, b, v, r1, r2, s, a1, a2, t;
char ch[4];
bool flag, flag1, flag2;
while(scanf("%d %d %d", &n, &p1, &p2) && (n+p1+p2))
{
flag
= true;
if(p1 == p2)flag = false;
for(i = 0; i < MAX; i++)root[i]=i,same[i]=diff[i]=relation[i]=_map[i]=0;
memset(dp,
0, sizeof(dp));
for (i = 0; i < n; i++)
{
scanf(
"%d %d %s", &a, &b, ch);
if(!flag)continue;
v
= (ch[0]=='y') ? 0:1;
r1
= findRoot(a), r2 = findRoot(b);
if(r1!=r2)
{
if(r1<r2){root[r2]=r1; relation[r2]=relation[a]^relation[b]^v;}
else {root[r1]=r2; relation[r1]=relation[a]^relation[b]^v;}
}
}
if(!flag){printf("no\n");continue;}
for(i=1; i<=(p1+p2); i++)
t
= findRoot(i),(relation[i]==0) ? (same[t]++) : (diff[t]++);
for (s=0,i=1;i<=(p1+p2);i++)
if(same[i]>0){_map[i]=s;node[s].s=same[i];node[s++].d=diff[i];}
a1
= min(node[0].d, node[0].s),a2 = max(node[0].d, node[0].s);
if(a1==a2){printf("no\n");continue;}
for(i=a1;i<a2;i++)dp[0][i]=a1;
for(i=a2;i<=p1;i++)dp[0][i]=a2;
for(i=1;i<s&&flag;i++)
{
a1
= min(node[i].s, node[i].d),a2 = max(node[i].s, node[i].d);
if(a1==a2){flag=false;break;}
for(j=a1;j<=p1;j++)
{
dp[i][j]
= dp[i-1][j-a1]?(dp[i-1][j-a1]+a1):0;
if(j-a2>0 && dp[i-1][j-a2])
{
t
= dp[i-1][j-a2]+a2;
if(t>dp[i][j])dp[i][j]=t;
}
}
}
if (dp[s-1][p1]!=p1)flag = false;
for(i=s-1,t=p1;i>0&&flag;i--)
{
flag1
= flag2 = false;
if(t > node[i].s && dp[i][t]-node[i].s == dp[i-1][t-node[i].s])
flag1
= true;
if(t > node[i].d && dp[i][t]-node[i].d == dp[i-1][t-node[i].d])
flag2
= true;
if(flag1&&flag2)flag=false;
if(flag1){node[i].c = 0; t-= node[i].s;}
else {node[i].c=1; t-= node[i].d;}
}
if(node[0].s == t)node[0].c = 0;
else node[0].c = 1;
if(flag)
{
for(i=1;i<=(p1+p2);i++)
if(node[_map[root[i]]].c==relation[i])printf("%d\n", i);
printf(
"end\n");
}
else printf("no\n");
}
return 0;
}

posted on 2010-12-07 20:35  ltang  阅读(1031)  评论(1编辑  收藏  举报

导航