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;
}
原文地址:https://www.cnblogs.com/minun/p/10473761.html