poj 1417 True Liars dp + 并查集

题意: 给出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;
}

猜你喜欢

转载自blog.csdn.net/c_cqq/article/details/81145482