poj 1417 - True Liars(并查集+背包)

来源:互联网 发布:观星软件 安卓 编辑:程序博客网 时间:2024/05/16 12:43

题意:

    题目中告诉两种人,一种只说真话,一种只说假话。然后告诉n个描述,每个描述是说a说b是说真话的人(yes)或者是说假话的人(no),最后问是否能判断哪些人是只说真话的那类人。

思路:

    根据题意可知:其中好人说真话,坏人说假话这点很重要。

    那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。

    如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。

    也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合

        然后我们通过并查集把有关系的一些人合并到一个集合内。

    然后问题就转化为:有n个集合,每个集合都有a, b连个数字,现在要求n个集合中各跳出一个数(a或者b),使得他们之和等于n1(说真话的人数)。

    然后用背包来解决这个问题,但答案要求输出所有说真话的人的编号,这就麻烦点了,我们要记录每个集合中的人的状态,还要记录dp的路径。具体方法见代码

代码如下:

const int M = 605;int p[M], flag[M], dp[M][M/2], left[M][M/2];int find(int x){    int tmp = p[x];    p[x] = (p[x] == x ? x : find(p[x]));    flag[x] = flag[tmp]^flag[x];    return p[x];}int main(){    int n, p1, p2, a, b, o;    char tmp[10];    while(scanf("%d%d%d", &n, &p1, &p2) && n+p1+p2)    {        for(int i = 1; i <= p1+p2; ++i) p[i] = i;        for(int i = 0; i < n; ++i)        {            scanf("%d%d %s", &a, &b, tmp);            o = strcmp("yes", tmp)==0?0:1;            int x = find(a);            int y = find(b);            if(x != y)            {                p[x] = y;                flag[x] = flag[a]^flag[b]^o;            }        }        vector<int>A[M];        vector<int>B[M];        int vis[M], cnt = 1;        memset(vis,0,sizeof(vis));        for(int i = 1; i <= p1+p2; ++i)        {            if(!vis[i])            {                int f = find(i);                for(int j = i; j <= p1+p2; ++j)                    if(find(j)==f)                    {                        vis[j] = 1;                        flag[j]==0?A[cnt].push_back(j):B[cnt].push_back(j);                    }                ++cnt;            }        }        memset(dp,0,sizeof(dp));        memset(left,0,sizeof(left));        dp[0][0] = 1;        for(int i = 1; i < cnt; ++i)        {            int la = A[i].size();            int lb = B[i].size();            for(int j = p1; j >= la||j >= lb; --j)            {                if(j>=la && dp[i-1][j-la]) dp[i][j] += dp[i-1][j-la], left[i][j] = j-la;                if(j>=lb && dp[i-1][j-lb]) dp[i][j] += dp[i-1][j-lb], left[i][j] = j-lb;            }        }        vector<int>ans;        if(dp[cnt-1][p1]!=1) printf("no\n");        else        {            int tt = p1;            for(int i = cnt-1; i >= 1; --i)            {                int dt = tt-left[i][tt];                if(dt==(int)A[i].size())                    for(int j = 0; j < (int)A[i].size(); ++j) ans.push_back(A[i][j]);                else                    for(int j = 0; j < (int)B[i].size(); ++j) ans.push_back(B[i][j]);                tt = left[i][tt];            }            sort(ans.begin(), ans.end());            for(int i = 0; i < (int)ans.size(); ++i) printf("%d\n", ans[i]);            printf("end\n");        }    }    return 0;}



原创粉丝点击