poj -- 1417 True Liars(并查集 + dp)

来源:互联网 发布:社交推荐算法 编辑:程序博客网 时间:2024/06/06 15:35

调了将近两天终于过了这道并查集 + 背包DP。。。!!!O(∩_∩)O~~

http://poj.org/problem?id=1417

题意就是有p1+p2个人,其中p1个好人,p2个坏人,好人说真话,坏人说假话。有n个关系X Y yes表示X说Y是好人,这样我们就知道X和Y都是好人或者都是坏人;X Y no表示X说Y是坏人,这就代表或者X是好人,Y是坏人,或者Y是好人而X是坏人。

我们将一个人拆成两个,X表示好人X,X+p1+p2表示坏人X,接着根据关系用并查集合并。这样就得到了一些集合,这些集合一定是两两相对的就是比如一个集合{X, Y +p1+p2, Z},那么就一定有一个集合是{X+p1+p2, Y, Z+p1+p2}。。。

接下来我们需要从这些集合中选择一些集合,使得他们的好人数之和为p1。在这里我们用背包的思想dp[i][j],表示前i个集合有j个好人的方案数。注意这里必定会选一个集合或者和他相对是集合,不可能都选,也不可能都不选。!!!!(QAQ卡在这里了。。。)

最后需要输出方案,根据dp的值dfs找即可。。

//#pragma comment(linker,"/STACK:1024000000,1024000000")#include <map>#include <set>#include <cmath>#include <queue>#include <stack>#include <cstdio>#include <string>#include <vector>#include <cstring>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;typedef pair<int, int> PII;#define pb push_back#define MP make_pair#define lson l, m, rt << 1#define rson m + 1, r, rt << 1 | 1const double eps = 1e-6;const int inf = 0x3f3f3f3f;const int mod = 1000000007;const int maxn = 1000 + 10;int Q, a, b, x, y;char str[5];vector<int> sum[maxn], V;int f[maxn], dp[maxn][500], A[maxn][2], ok[maxn];void init(int  k){    for(int i=0; i<=k; i++) {f[i] = i; sum[i].clear(); A[i][0] = A[i][1] = 0;}}int findset(int x){    return x == f[x] ? x : f[x] = findset(f[x]);}void judge(int x, int y){    int xx = findset(x);    int yy = findset(y);    if(xx != yy) f[xx] = yy;}void dfs(int k, int n){    if(k == 2) {        if(A[k][0] == n) ok[k] = 0;        else if(A[k][1] == n) ok[k] = 1;        return;    }    if(n - A[k][0] >= 0 && dp[k - 2][n - A[k][0]] == 1){        dfs(k - 2, n - A[k][0]);        ok[k] = 0; return;    }    else if(n - A[k][1] >= 0 && dp[k - 2][n - A[k][1]] == 1) {        dfs(k - 2, n - A[k][1]);        ok[k] = 1; return;    }}int main(){    while(scanf("%d%d%d", &Q, &a, &b) == 3){        if(!Q && !a && !b) break;        int n = a + b;        init(2 * n);        for(int i=0; i<Q; i++){            scanf("%d%d%s", &x, &y, str);            if(str[0] == 'n'){                judge(x, y + n);                judge(x + n, y);            }            else if(str[0] == 'y'){                judge(x, y);                judge(x + n, y + n);            }        }        int num = 1;        map<int, int> mm;        mm.clear();        for(int i=1; i<=n; i++){            int k1 = findset(i), k2 = findset(i + n);            if(mm[k1] == 0){//重新编号。。一个集合的反向集合是他的mm值+1(或-1)                mm[k1] = num++;                mm[k2] = num++;            }            A[mm[k1]][0]++;//haoren            A[mm[k2]][1]++;//huairen            sum[mm[k1]].pb(i);//保存每个集合的好人        }        memset(dp, 0, sizeof(dp));        dp[0][0] = 1;        for(int i=2; i<num; i+=2){            for(int j=n; j>=0; j--){                if(j >= A[i][0]) dp[i][j] += dp[i - 2][j - A[i][0]];                if(j >= A[i][1]) dp[i][j] += dp[i - 2][j - A[i][1]];//                printf("dp[%d][%d] = %d\n", i, j, dp[i][j]);            }        }        if(dp[num - 1][a] != 1) puts("no");        else{            dfs(num - 1, a);            V.clear();            for(int i=2; i<num; i+=2){                if(ok[i] == 0){                    for(unsigned j=0; j<sum[i].size(); j++)                        V.pb(sum[i][j]);                }                else if(ok[i] == 1){                    for(unsigned j=0; j<sum[i - 1].size(); j++)                        V.pb(sum[i - 1][j]);                }            }            sort(V.begin(), V.end());            for(unsigned i=0; i<V.size(); i++){                printf("%d\n", V[i]);            }            puts("end");        }    }    return 0;}

0 0
原创粉丝点击