zoukankan      html  css  js  c++  java
  • POJ 1417 True Liars(种类并查集+dp背包问题)

    题目大意:

    一共有p1+p2个人,分成两组,一组p1,一组p2。给出N个条件,格式如下:

    x y yes表示x和y分到同一组,即同是好人或者同是坏人。

    x y no表示x和y分到不同组,一个为好人,一个为坏人

    这个可以自己分析一下, 很简单。

    问分组情况是否唯一,若唯一则输出p1的成员,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。

     

    思路:

    参考链接:http://www.cnblogs.com/kuangbin/archive/2013/04/06/3002498.html

     

    用rel[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。

    (可以举类验证一下,假如1、2、3,前者分别是后者的父亲。yes=0,no=1,这样才能满足:3相对2的关系+2相对1的关系=3相对1的关系。如果yes=1,no=0,则关系不成立)

    处理之后,还需要判断是否唯一。

    我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,只知道节点相对根节点的关系,0为一组,1为另一组)

    接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。

    背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种。

    如果dp[cnt][p1]!=1说明方案不唯一,或者无解。

     输出方案就是加个pre数组,从后往前递推。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    #include <vector>
    
    using namespace std;
    const int maxn=610;
    int father[maxn];
    int rel[maxn]; //rel[i]表示i与根节点的关系,0表示i与根节点在相同集合,1表示i与根节点在不同集合
    vector<int> b[maxn][2]; //b[i][0]存储与i在相同集合的点,b[i][1]存储与i在不同集合的点
    int a[maxn][2];  //a[i][0]存储与i在相同集合的点的个数,a[i][1]存储与i在不同集合的点的个数
    int num[maxn][2]; //num[i][0]存储与i在相同集合的点的个数(包括i),num[i][1]存储与i在不同集合的个数(包括i)
    int dp[maxn][310]; //dp[i][j]表示前i组选取j个人为好人的方案数。
     //用于最后输出好人的编号,pre[i][j]对应dp[i][j],存储的是从第i组选的好人个数(只有两种情况,要么是好人的个数,要么是坏人的个数)
    int pre[maxn][310];
    int vis[maxn];
    int n,p1,p2,cnt;
    
    void init(){
        //i应该是<=p1+p2,一开始写为n了。。。
        for(int i=0;i<=p1+p2;i++){
            father[i]=i;
            rel[i]=0;  //一开始初始化为1了,囧。。。
        }
    }
    int find_root(int x){
        if(father[x]==x)
            return x;
        int fa=father[x];
        father[x]=find_root(father[x]);
        rel[x]=(rel[x]+rel[fa])%2;
        return father[x];
    }
    void Union(int a,int b){
        father[b]=a;
    }
    
    int dfs(int i,int num){
        //该条件一开始都忘写了,结果导致超时。。。
        if(dp[i][num]!=-1){
            return dp[i][num];
        }
        if(i==0){
            if(a[i][0]==a[i][1]){
                if(num==a[i][0])
                    dp[i][num]=2;
                else
                    dp[i][num]=0;
            }
            else if( num==a[i][0] || num==a[i][1]){
                dp[i][num]=1;
                pre[i][num]=num;  //这里都忘记写了,额
            }
            else{
                dp[i][num]=0;
            }
            return dp[i][num];
        }
        dp[i][num]=0;
        if(num>=a[i][0] && dfs(i-1,num-a[i][0])){
            dp[i][num]+=dp[i-1][num-a[i][0]];
            pre[i][num]=a[i][0];
        }
        if(num>=a[i][1] && dfs(i-1,num-a[i][1])){
            dp[i][num]+=dp[i-1][num-a[i][1]];
            pre[i][num]=a[i][1];
        }
    
        return dp[i][num];
    
    }
    int main()
    {
        int x,y,k,tmp1,tmp2;
        char ch[5];
        while(scanf("%d%d%d",&n,&p1,&p2)){
            if(n==0 && p1==0 && p2==0)
                break;
            init();
            for(int i=1;i<=n;i++){
                scanf("%d%d%s",&x,&y,ch);
                int fx=find_root(x);
                int fy=find_root(y);
                if(ch[0]=='y')
                    k=0;
                else
                    k=1;
                if(fx!=fy){
                    Union(fx,fy);
                    rel[fy]=(rel[y]+k+rel[x])%2;
                }
            }
            for(int i=0;i<maxn;i++){
                b[i][0].clear();
                b[i][1].clear();
                a[i][0]=0;
                a[i][1]=0;
            }
            memset(vis,0,sizeof(vis));
            cnt=0;
            //获取有哪些集合
            for(int i=1;i<=p1+p2;i++){
                if(!vis[i]){
                    tmp1=find_root(i);
                    for(int j=i;j<=p1+p2;j++){
                        tmp2=find_root(j);
                        if(tmp2==tmp1){
                            vis[j]=1;
                            b[cnt][rel[j]].push_back(j);
                            a[cnt][rel[j]]++;
                        }
                    }
                    cnt++;
                }
            }
            memset(dp,-1,sizeof(dp));
            if(dfs(cnt-1,p1)!=1){
                printf("no
    ");
            }
            else{
                vector<int> ans;
                int tmp=p1,t;
                for(int i=cnt-1;i>=0;i--){
                    t=pre[i][tmp];
                    //如果选的是与根节点在同一集合的元素
                    if(t==a[i][0]){
                        for(int j=0;j<b[i][0].size();j++){
                            ans.push_back(b[i][0][j]);
                        }
                    }
                    //如果选的是与根节点不在同一集合的元素
                    else if(t==a[i][1]){
                        for(int j=0;j<b[i][1].size();j++){
                            ans.push_back(b[i][1][j]);
                        }
                    }
                    tmp=tmp-t;
                }
                sort(ans.begin(),ans.end());
                for(int i=0;i<ans.size();i++){
                    printf("%d
    ",ans[i]);
                }
                printf("end
    ");
            }
    
        }
        return 0;
    }
  • 相关阅读:
    linux学习
    linux学习--常用基本命令
    expect学习
    linux学习-cut,ssh keygen ,find
    expect
    告别痛苦,快乐学习Pandas!开源教程《Joyful-Pandas》发布
    iBooker 财务提升星球 2020.2~3 热门讨论
    计算机电子书 2020 CDNDrive 备份(预览版)
    poj2017
    poj2000
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3298767.html
Copyright © 2011-2022 走看看