1. 程式人生 > >True Liars 【POJ - 1417】【種類並查集+0-1揹包】

True Liars 【POJ - 1417】【種類並查集+0-1揹包】

題目連結


  題目想要知道有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;
}