POJ1417 True Liars (並查集+揹包)

題目鏈接:http://poj.org/problem?id=1417


大致題意:有兩類人,好人和壞人,好人只會說真話,壞人只會說假話,有k組詢問,p1個好人,p2個壞人。每組向a詢問b是否爲好人,得到答覆yes或no。問通過已知詢問是否能夠判斷出所有的好人。若不能輸出"no",可以則輸出代表好人的編號。


在看了博客文章之後纔有比較清晰的思路,感覺自己對綜合性稍強的題目就已經沒什麼辦法了,還需要學習

思路:利用帶權並查集,將所有人分組。如果回答爲yes則無論如何a與b均爲同一類人,否則不同類。分類後,遍歷每一類人,用揹包求出能夠組合出p1人的方案數量。如果數量不爲1則說明沒有確定所有的好人。否則則可以求出所有好人的編號。以每組人數爲路徑標識按路徑將每組人的編號求出來。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 605;

int k,p1,p2,n;
int fa[N],offset[N];
int num[N][4];
bool vis[N];
int dp[N][N];
int path[N][N];

vector<int> vec[N][4];

void init()
{
    for (int i=0;i<N;i++)
    {
        fa[i] = i;
        offset[i] = 0;
    }
    memset(dp,0,sizeof (dp));
    memset(vis,false,sizeof (vis));
}

int fnd(int x)
{
    if (x!=fa[x])
    {
        int t = fa[x];
        fa[x] = fnd(fa[x]);
        offset[x] = (offset[x] + offset[t])%2;
    }
    return fa[x];
}

void read()
{
    int ofst;
    int x,y;
    char re[20];
    scanf("%d%d%s",&x,&y,&re);
    if (re[0] == 'y')
        ofst = 0;
    else ofst = 1;

    int fx = fnd(x);
    int fy = fnd(y);
    if (fx!=fy)
    {
        fa[fx] = fy;
        offset[fx] = (offset[y] + ofst - offset[x] + 2)%2;
    }
}

int main()
{
    while (scanf("%d%d%d",&k,&p1,&p2)==3)
    {
        init();
        if (k==0 && p1==0 && p2==0) break;
        n = p1 + p2;
        for (int i=0;i<k;i++)
        {
            read();
        }

        for (int i=0;i<N;i++)
        {
            num[i][0] = 0;
            num[i][1] = 0;
            vec[i][1].clear();
            vec[i][0].clear();
        }

        int cnt = 1;

        for (int i=1;i<=n;i++)
        {
            if (!vis[i])
            {
                int t = fnd(i);
                for (int j=i;j<=p1+p2;j++)
                {
                    if (fnd(j)==t)
                    {
                        vis[j] = true;
                        num[cnt][offset[j]]++;
                        vec[cnt][offset[j]].push_back(j);
                    }
                }
                cnt++;
            }
        }

        dp[0][0] = 1;
        for (int i=1;i<cnt;i++)
        {
            for (int j=p1;j>=0;j--)
            {
                if (j>=num[i][0] && dp[i-1][j - num[i][0]])
                {
                    dp[i][j] += dp[i-1][j - num[i][0]];
                    path[i][j] = j - num[i][0];
                }
                if (j>=num[i][1] && dp[i-1][j - num[i][1]])
                {
                    dp[i][j] += dp[i-1][j - num[i][1]];
                    path[i][j] = j - num[i][1];
                }
            }
        }
        vector<int> ans;
        if (dp[cnt-1][p1]!=1) printf("no\n");
        else
        {
            ans.clear();
            int t = p1;
            for (int i=cnt-1; i>=0; i--)
            {
                int tmp = t - path[i][t];
                if (tmp == num[i][0])
                {
                    int len = vec[i][0].size();
                    for (int pos=0; pos<len; pos++)
                    {
                        ans.push_back(vec[i][0][pos]);
                    }
                }
                else
                {
                    int len = vec[i][1].size();
                    for (int pos=0; pos<len; pos++)
                    {
                        ans.push_back(vec[i][1][pos]);
                    }
                }
                t = path[i][t];
            }
            sort(ans.begin(),ans.end());
            int len = ans.size();
            for (int i=0;i<len;i++)
                printf("%d\n",ans[i]);
            printf("end\n");
        }
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章