题目:
http://poj.org/problem?id=1417
题意:
有两种人:好人和坏人,其中好人说的话一定是真的,坏人说的话一定是假的。现在知道好人和坏人的具体个数,又提问了n个问题:x y yes|no,问第x个人,第y个人是好人还是坏人,回答yes或者no。问根据以上能不能判断出哪些人是好人,题目保证不会有矛盾的问答
思路:
可以根据问答把所有人分类,每类又分成两小类:相互矛盾的两小类,相互矛盾的两小类必定有一类是好人一类坏人,但具体哪一类是好人哪一类是坏人并不知道。然后可以就是看能不能从每一类中当且仅当拿出一个小类凑一起,使人数恰好等于好人的人数,且这个方案数只有一种。可以发现,分类用带权并查集实现,后面凑人数用背包记录方案数和路径可以实现。本人的代码写的太挫了,惨不忍睹,不想改了 。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
const int N = 700 + 10, INF = 0x3f3f3f3f;
int par[N], rnk[N];
int w[N][2];
int dp[N][N], pre[N][N], p[N], rp[N];
void init(int n)
{
for(int i = 1; i <= n; i++) par[i] = i, rnk[i] = 0;
}
int ser(int x)
{
if(x != par[x])
{
int fx = ser(par[x]);
rnk[x] = (rnk[x] + rnk[par[x]]) % 2;
par[x] = fx;
}
return par[x];
}
void unite(int x, int y, int type)
{
int fx = ser(x), fy = ser(y);
if(fx == fy) return;
rnk[fy] = (rnk[y] + type + rnk[x]) % 2;
par[fy] = fx;
}
int main()
{
int n, p1, p2;
while(scanf("%d%d%d", &n, &p1, &p2), n || p1 || p2)
{
init(p1 + p2);
int x, y;
char s[10];
for(int i = 1; i <= n; i++)
{
scanf("%d%d%s", &x, &y, s);
if(s[0] == 'y') unite(x, y, 0);
else unite(x, y, 1);
}
memset(w, 0, sizeof w);
memset(p, -1, sizeof p);
int cnt = 0, id;
for(int i = 1; i <= p1+p2; i++)//分类,统计每小类的人数作为物品
{
ser(i);
if(p[par[i]] != -1) id = p[par[i]];
else id = p[par[i]] = ++cnt;
w[id][rnk[i]]++;
}
memset(dp, 0, sizeof dp);
memset(pre, -1, sizeof pre);
dp[0][0] = 1;
for(int i = 1; i <= cnt; i++) //背包求方案数
for(int j = p1; j >= 0; j--)
{
if(j - w[i][0] >= 0)
{
dp[i][j] += dp[i-1][j-w[i][0]];
if(dp[i-1][j-w[i][0]] != 0) pre[i][j] = 0;
}
if(j - w[i][1] >= 0)
{
dp[i][j] += dp[i-1][j-w[i][1]];
if(dp[i-1][j-w[i][1]] != 0) pre[i][j] = 1;
}
}
if(dp[cnt][p1] != 1) //恰好为1说明可以判断,否则不能
{
printf("no\n"); continue;
}
memset(rp, -1, sizeof rp);
for(int i = 1; i <= p1+p2; i++) //rp[i]表示第i件物品所属的类别,是p[i]的反向映射
if(p[i] != -1) rp[p[i]] = i;
int res[N][2], k = 0, tm = p1;
memset(res, -1, sizeof res);
for(int i = cnt; i >= 1; i--)//背包求路径
if(pre[i][tm] != -1)
{
int t = pre[i][tm];
res[k++][t] = rp[i];
tm -= w[i][t];
}
int ans[N], tot = 0;
for(int i = 1; i <= p1+p2; i++)
{
for(int j = 0; j < k; j++)
{
if(res[j][0] != -1 && par[i] == res[j][0] && rnk[i] == 0)
{
ans[tot++] = i; break;
}
if(res[j][1] != -1 && par[i] == res[j][1] && rnk[i] == 1)
{
ans[tot++] = i; break;
}
}
}
sort(ans, ans + tot);
for(int i = 0; i < tot; i++) printf("%d\n", ans[i]);
puts("end");
}
return 0;
}