POJ 1417 True Liars
2010-12-07 20:35
363 查看
解题思路:并查集+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公式中 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)应该只有唯一的路径
分别表示选取同类与不同类两种情况,我们必须满足二者仅有一个等式满足
欢迎review
分析易知:如果答案为"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; }
相关文章推荐
- poj 1417 - True Liars(并查集+背包)
- POJ 1417 True Liars(种类并查集+dp背包问题)
- poj 1417 True Liars(并查集+DP)
- F - True Liars POJ 1417(并查集)(DFS)
- POJ 1417 True Liars(并查集+DP)
- POJ 1417 True Liars(并查集+DP)
- True Liars POJ - 1417
- POJ1417 True Liars(DP)
- POJ 1417 True Liars(种类并查集+dp+路径输出)
- poj 1417 True Liars 解题报告 并查集 DP
- POJ 1417 True Liars(路径压缩并查集+DP背包问题)
- poj -- 1417 True Liars(并查集 + dp)
- POJ 1417 True Liars(并查集+DP)
- POJ - 1417 并查集+背包
- 【POJ1417】【True Liars】【加权并查集+背包+输出路径】
- POJ 1417 True Liars
- POJ 1417 True Liars
- ACM篇:POJ 2572 -- Hard to Believe, But True!
- F - True Liars - poj1417(背包+并查集)
- POJ1417 True Liars (并查集+背包)