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

调了将近两天终于过了这道并查集 + 背包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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

typedef long long ll;
typedef pair PII;

#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const 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 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 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=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

你可能感兴趣的:(数据结构,DP,并查集)