zoukankan      html  css  js  c++  java
  • POJ 1417 True Liars (并查集+dp+路径输出)

    题解思路:

    通过题意可以推出如果回答为yes,则a b同族,no为不同族;

    并查集维护,可以知道有几棵树;

    用cnt[i][1 / 0]表示每棵树里2类人各有多少;

    看能否拼出为一个解;

    dp[i][j]表示前i棵树,可以拼成j个人的个数,

    转移方程 dp[i][j]=dp[i-1][j-cnt[i][0]],dp[i-1][j-cnt[i][1]])注意这里是俩类人力必选一类;

    dp倒退回去,记录路径输出;

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<map>
    #include<queue>
    #include<stack>
    #include<set>
    
    #define ll long long
    #define mem(a,b) memset(a,b,sizeof(a))
    
    const int maxn=600+50;
    
    using namespace std;
    
    int f[maxn],sum[maxn],d[maxn][301],cnt[maxn][2],vis[maxn],choose[maxn];
    //   父      路径       dp          记录每类人的数量 重新标号   选择了0/1
    
    int root(int x)
    {
        if(f[x]==x) return x;
        int rt=root(f[x]);
        sum[x]^=sum[f[x]];
        return f[x]=rt;
    }
    
    void init()
    {
        mem(sum,0);
        mem(d,0);
        mem(cnt,0);
        mem(vis,0);
        mem(choose,0);
    }
    
    int main(){
        int n,s1,s2,a,b,x,y,v,tot;char s[5];
        while(~scanf("%d%d%d",&n,&s1,&s2))
        {
            if(n+s1+s2==0) break;
            tot=0;
            init();
            for(int i=1;i<=s1+s2;i++) f[i]=i;
            for(int i=1;i<=n;i++)
            {
                scanf("%d%d%s",&a,&b,s);
                s[0]=='y'?v=0:v=1;
                x=root(a);
                y=root(b);
                if(x!=y)
                {
                    f[x]=y;
                    sum[x]=sum[a]^sum[b]^v;
                }
            }
            for(int i=1;i<=s1+s2;i++)//重新标号
            {
                if(!vis[root(i)]) vis[f[i]]=++tot;
                if(sum[i]==0)
                    cnt[vis[f[i]]][0]++;
                else
                    cnt[vis[f[i]]][1]++;
            }
    //        for(int i=1;i<=tot;i++)
    //        {
    //            cout<<cnt[i][0]<<" "<<cnt[i][1]<<endl;
    //        }cout<<endl;
            d[0][0]=1;
            for(int i=1;i<=tot;i++)//dp
            {
                for(int j=0;j<=s1;j++)
                {
                    if(j>=cnt[i][1])
                        d[i][j]+=d[i-1][j-cnt[i][1]];
                    if(j>=cnt[i][0])
                        d[i][j]+=d[i-1][j-cnt[i][0]];
                }
            }
    //        for(int i=1;i<=tot;i++)
    //        {
    //            for(int j=1;j<=s1;j++)
    //                cout<<d[i][j]<<" ";
    //            cout<<endl;
    //        }
            if(d[tot][s1]==1)
            {
                int l=s1;
                for(int i=tot;i>0;i--)//倒推路径
                {
                    if(d[i][l]==d[i-1][l-cnt[i][1]])
                    {
                        choose[i]=1;
                        l-=cnt[i][1];
                    }
                    else if(d[i][l]==d[i-1][l-cnt[i][0]])
                    {
                        choose[i]=0;
                        l-=cnt[i][0];
                    }
                }
                for(int i=1;i<=s1+s2;i++)
                {
                    int x=vis[f[i]];
                    int y=sum[i];
                    if(choose[x]==y)
                    {
                        printf("%d
    ",i);
                    }
                }
                printf("end
    ");
            }
            else printf("no
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    20201215王馨瑶 实验一《Python程序设计》实验报告
    20201215第十六周学习总结
    python笔记
    信导笔记
    成绩调节
    2020-2021-1 20201226 《信息安全专业导论》第十三周学习总结
    链表(补交)
    2020-2021-1 20201226 《信息安全专业导论》第十二周学习总结
    Wireshark 实践
    ssh
  • 原文地址:https://www.cnblogs.com/minun/p/10473761.html
Copyright © 2011-2022 走看看