True Liars 【POJ - 1417】【种类并查集+0-1背包】

版权声明:https://blog.csdn.net/qq_41730082 https://blog.csdn.net/qq_41730082/article/details/85102261

题目链接


  题目想要知道有P个好人(说真话的人)和Q个坏人(说假话的人),并且有N条信息,代表A说B是好人(yes)、坏人(no),那么,在保证答案唯一的情况下输出这P个好人,并且最后的时候输出“end”,否则,输出“no”。坑点:答案唯一指的是最后你要确保它答案唯一,不是题目说答案唯一,如果答案不唯一,你得输出“no”!!!

  我的想法就是,既然有N条关系链,就是在变相的告诉我们了这P+Q个人的关系,对于一个说假话的人,他对应“yes”的人肯定是跟他一伙的,“no”就一定不是一伙的;同理,说真话的人。——这样,我们可以确定每棵树下的阵营关系。

  然后,对于每棵树,都有好人与坏人的数量,可以说是确定了两个阵营的人的数量,我们既要选取了,若是选取阵营一的,就是把阵营二的人当作是坏人对待,若是选取阵营二的就是反之了。所以,这就是个背包问题了。但这个背包还不简单,我们得记录路径和知晓答案的唯一性

  知晓答案的唯一性:我们可以通过到达最终状态的可能性的多少来解,不如列写一个dp[][],表示dp[访问到第几棵树][已经有多少好人]如此向上推进。那么,如果dp[树的总数][P]不为1,就直接输出“no”就是了,不为1,就代表了可能不止一个解,或者没有解。

  接下来,就是处理路径,所谓的输出好人:可以知道,好人有P个,并且,方案数唯一,就意味着如果此时的点的方案数不唯一,我们就不能选择它,也是必须选择方案固定的点,并且还要是能到达的点,对于一个点,我们有这样的考虑,如果它的值是小于等于剩余的可选好人数量,不能选,此时可以去考虑选择它的对立面,就是对立阵营,并且,还必须满足唯一方案。


#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define efs 1e-7
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 607;
int N, tot, P, Q, root[maxN], num[maxN];    //P个好人,Q个坏人
int L, R, child[maxN][2][maxN], shu[maxN][2];
bool vis[maxN];
int gen[maxN], sum_gen, dp[maxN][maxN], ans[maxN], sum_ans;
bool cmp(int e1, int e2) { return e1 < e2; }
char op[10];
vector<int> vt[maxN][2];
int fid(int x)
{
    if(x == root[x]) return x;
    int tmp = root[x];
    root[x] = fid(root[x]);
    num[x] = (num[x] + num[tmp] + 2)%2;
    return root[x];
}
void init()
{
    for(int i=0; i<maxN; i++) root[i] = i;
    memset(num, 0, sizeof(num));
    memset(shu, 0, sizeof(shu));
    memset(vis, false, sizeof(vis));
    memset(dp, 0, sizeof(dp));
    tot = P + Q;
    sum_gen = sum_ans = 0;
    for(int i=1; i<maxN; i++)
    {
        for(int j=0; j<=1; j++)
        {
            vt[i][j].clear();
        }
    }
}
int main()
{
    while(scanf("%d%d%d", &N, &P, &Q) && (N || P || Q))
    {
        init();
        for(int i=1; i<=N; i++)
        {
            scanf("%d%d%s", &L, &R, op);
            if(P == Q) continue;        //目的:唯一确定!
            if(L == R) continue;
            int u = fid(L), v = fid(R);
            if(op[0] == 'y')
            {
                if(u != v)
                {
                    root[u] = v;
                    num[u] = (num[R] - num[L] + 2)%2;
                }
            }
            else
            {
                if(u != v)
                {
                    root[u] = v;
                    num[u] = (num[R] - num[L] + 1 + 2)%2;   //因为是no,所以一定在不同的阵营
                }
            }
        }
        for(int i=1; i<=tot; i++)
        {
            int tmp = fid(i);
            if(!vis[tmp])
            {
                vis[tmp] = true;
                gen[++sum_gen] = tmp;   //处理这么多个根,根离散化后的值
            }
            child[tmp][num[i]][++shu[tmp][num[i]]] = i; //以tmp为根的,num[]为阵营的
            vt[tmp][num[i]].push_back(i);
        }
        dp[0][0] = 1;
        for(int i=1; i<=sum_gen; i++)
        {
            for(int k=0; k<=1; k++)
            {
                for(int j=P; j>=shu[gen[i]][k]; j--)
                {
                    dp[i][j] += dp[i-1][j-shu[gen[i]][k]];
                }
            }
        }
        if(dp[sum_gen][P] != 1) { printf("no\n"); continue; }   //没有方案数,或方案数不唯一
        for(int i=sum_gen; i>=1; i--)
        {
            if(P-shu[gen[i]][0]>=0 && Q-shu[gen[i]][1]>=0 && dp[i-1][P-shu[gen[i]][0]])
            {
                for(int j=1; j<=shu[gen[i]][0]; j++)
                {
                    ans[++sum_ans] = child[gen[i]][0][j];
                }
                P -= shu[gen[i]][0];
                Q -= shu[gen[i]][1];
            }
            else if(P-shu[gen[i]][1]>=0 && Q-shu[gen[i]][0]>=0 && dp[i-1][P-shu[gen[i]][1]])
            {
                for(int j=1; j<=shu[gen[i]][1]; j++)
                {
                    ans[++sum_ans] = child[gen[i]][1][j];
                }
                P -= shu[gen[i]][1];
                Q -= shu[gen[i]][0];
            }
        }
        sort(ans+1, ans+1+sum_ans, cmp);
        for(int i=1; i<=sum_ans; i++) printf("%d\n", ans[i]);
        printf("end\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/85102261