题意: 给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。
其中比较重要的是,好人总说真话,坏人总说假话。不会存在矛盾情况。请问你是否存在唯一解,如果存在请输出唯一解。
由题意知:如果A说B是好人,那么A与B是同一类人。如果A说B是坏人,那么A与B不同类。所以原题所给的每句话就是一条路径压缩并查集的关系,那么我们最终合并所有关系可以得到cnt个连通分量且每个分量中节点的相互关系(同类or异类)我们都知道。
假设有cnt个连通分量,第一个分量有x1与y1个两类人,第二个分量有x2与y2个两类人...所以我们现在想知道是否只有一种方式让我们从第一分量中抓X1(或Y1)个人来,从第二分量中抓X2(或Y2)人来...直到每个分量都抓一类人时,正好抓了p1个人。
如果只有1种方式实现上面的目的,那么就有唯一解。记录DP的每次选择最后输出即可。
令d[i][j]表示取完前i个分量后正好j人的方法数,我们最后要求的是看d[cnt][p1]是否==1即可
链接:poj - 1417
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1005;
const int maxm = 100500;
int bag[maxn][2];
int cnt;
map<int, int> mp;
int f[maxn];
int v[maxn];
int ins(int x, int b) {
if(mp.find(x) == mp.end()) {
mp[x] = ++cnt;
}
bag[mp[x]][b]++;
return mp[x];
}
int findset(int i) {
if(f[i] == -1) {
return i;
}
int temp = findset(f[i]);
v[i] = (v[i] + v[f[i]]) % 2;
return f[i] = temp;
}
void binds(int i, int j, int temp) {
int a = findset(i);
int b = findset(j);
if (a != b) {
f[b] = a;
v[b] = (v[i] + v[j] + temp) % 2;
}
}
int dp[maxn][305];
int main ()
{
int n, p1, p2;
while(~scanf("%d %d %d", &n, &p1, &p2) != EOF) {
if(n == 0 && p1 == 0 && p2 == 0) {
break;
}
cnt = 0;
mp.clear();
memset(bag, 0, sizeof(bag));
memset(f, -1, sizeof(f));
memset(v, 0, sizeof(v));
memset(dp, 0, sizeof(dp));
while(n--) {
int a, b, temp;
char str[10];
scanf("%d %d %s", &a, &b, str);
if(str[0] == 'y') {
temp = 0;
}
if(str[0] == 'n') {
temp = 1;
}
binds(a, b, temp);
}
for(int i = 1; i <= p1 + p2; i++) {
int fi = findset(i);
ins(fi, v[i]);
}
dp[0][0] = 1;
for(int i = 1; i <= cnt; i++) {
for(int j = 0; j <= p1; j++) {
if(j >= bag[i][0]) {
dp[i][j] = dp[i - 1][j - bag[i][0]];
}
if(j >= bag[i][1]) {
dp[i][j] += dp[i - 1][j - bag[i][1]];
}
}
}
if(dp[cnt][p1] == 1) {
int j = p1;
int ch[maxn];
memset(ch, -1, sizeof(ch));
for(int k = cnt; k >= 1; k--) {
if(dp[k][j] == dp[k - 1][j - bag[k][0]]) {
ch[k] = 0;
j = j-bag[k][0];
}
else if(dp[k][j] == dp[k - 1][j - bag[k][1]]) {
ch[k] = 1;
j = j - bag[k][1];
}
}
for(int i = 1; i <= p1 + p2; i++) {
int fa = findset(i);
int num = mp[fa];
if(v[i] == ch[num]) {
printf("%d\n", i);
}
}
puts("end");
}
else {
puts("no");
}
}
return 0;
}