poj 1417 True Liars 带权并查集+母函数dp

并查集求出每个集合的与祖宗是同一类人的数量和不是同一类人的数量然后只能选其中之一或不选,然后背包并求具体选择方案
按网上题解写的….

#include<cstdio>
#include<vector>
#include<utility>
#include<cstring>
#define maxn 605
using namespace std;
int dp[maxn][maxn],fa[maxn],lin[maxn],va[maxn][2],ha[maxn],sym[maxn][maxn];
int last[maxn][maxn];
int n,p1,p2;
char s[10];
void init()
{
     for(int i=0;i<=p1+p2;i++)fa[i] = i;
     memset(dp,0,sizeof(dp));
     memset(va,0,sizeof(va));
     memset(lin,0,sizeof(lin));
     memset(last,0,sizeof(last));
     memset(sym,0,sizeof(sym));
}
int getFarther(int i)
{
    if(i==fa[i])return i;
    int pre = fa[i];
    fa[i] = getFarther(fa[i]);
    lin[i]^=lin[pre];
    return fa[i];
}
int main()
{
    while(scanf("%d%d%d",&n,&p1,&p2)!=EOF)
    {
        if(n==0&&p1==0&&p2==0)break;
        int l = 0;
        init();
        while(n--)
        {
            int flag,a,b;
            scanf("%d %d %s",&a,&b,s);

            if(s[0]=='y')flag = 0;
            else flag = 1;

            int faa = getFarther(a),fab = getFarther(b);
            if(faa!=fab)
            {
                fa[fab] = faa;
                lin[fab] = lin[b]^flag^lin[a];
            }
        }

        for(int i=1;i<=p1+p2;i++) if(getFarther(i)==i)ha[i] = ++l;

        for(int i=1;i<=p1+p2;i++) va[ha[getFarther(i)]][lin[i]]++;

        dp[0][0] = 1;
        for(int i=1;i<=l;i++)
            for(int j=0;j<=p1;j++)
                for(int k=0;k<=1;k++)
                    if(j-va[i][k]>=0&&dp[i-1][j-va[i][k]])
                    {
                           dp[i][j]+=dp[i-1][j-va[i][k]];
                           last[i][j] = va[i][k];
                    }
        if(dp[l][p1]!=1)printf("no\n");
        else{
            int pre = p1;
            for(int i=l;i>=1;i--)
            {
                //printf("%d\n",pre);
                if(!pre)break;
                if(last[i][pre]==va[i][0])sym[i][0]++;
                else sym[i][1]++;
                pre-=last[i][pre];
            }
             for(int i=1;i<=p1+p2;i++)
                 if(sym[ha[getFarther(i)]][lin[i]])printf("%d\n",i);
             printf("end\n");
        }
    }
    return 0;
}

自己xjb搞得至今感觉对但就是过不了。。。先记录一下

#include<cstdio>
#include<vector>
#include<utility>
#include<cstring>
#define maxn 2005
using namespace std;
int dp[maxn],fa[maxn],link[maxn],va[maxn][3],ha[maxn],ha2[maxn];
pair<int,int>last[maxn];
int n,p1,p2;
char s[10];
void init()
{
     for(int i=0;i<=p1+p2;i++)fa[i] = i;
     memset(dp,0,sizeof(dp));
     memset(va,0,sizeof(va));
     memset(ha,0,sizeof(ha));
     memset(ha2,0,sizeof(ha2));
     memset(link,0,sizeof(link));
     memset(last,0,sizeof(last));
}
int getFarther(int i)
{
    if(i==fa[i])return i;
    int pre = fa[i];
    fa[i] = getFarther(fa[i]);
    link[i]^=link[pre];
    return fa[i];
}
int main()
{
    while(scanf("%d%d%d",&n,&p1,&p2)!=EOF)
    {
        if(n==0&&p1==0&&p2==0)break;
        int l = 0;
        init();
        while(n--)
        {
            int flag,a,b;
            scanf("%d %d %s",&a,&b,s);

            if(s[0]=='y')flag = 0;
            else flag = 1;

            int faa = getFarther(a),fab = getFarther(b);
            if(faa!=fab)
            {
                fa[fab] = faa;
                link[fab] = link[b]^flag^link[a];
            }
        }

        for(int i=1;i<=p1+p2;i++) if(getFarther(i)==i) {ha[i] = ++l,ha2[l] = i;}

        for(int i=1;i<=p1+p2;i++) va[ha[getFarther(i)]][link[i]]++;

        dp[0] = 1;
        for(int i=1;i<=l;i++)
            for(int j=p1;j>=0;j--)
                for(int k=0;k<=1;k++)
                    if(va[i][k]&&j-va[i][k]>=0&&dp[j-va[i][k]])
                    {
                       dp[j] += dp[j-va[i][k]];
                       if(dp[j]==1)
                       {
                        last[j].first = i;
                        last[j].second = k;
                       }
                       if(dp[j]>1)dp[j] = 2;
                    }
        if(dp[p1]!=1)printf("no\n");
        else{
             vector<pair<int,int> >ve;
             int pre = p1;
             while(pre)
             {
                 ve.push_back(make_pair(ha2[last[pre].first],last[pre].second));
                 pre-=va[last[pre].first][last[pre].second];
             }
             for(int i=1;i<=p1+p2;i++)
                 for(int j=0;j<ve.size();j++)
                     if(ve[j].first==getFarther(i)&&link[i]==ve[j].second)
                     {
                       printf("%d\n",i);
                     }
            printf("end\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值