poj-1417 并查集01模型+背包dp

poj-1417 ❤️ 并查集01模型+背包dp

题意:

有n次对话,p1个好人,p2个坏人,问x2是一个好人吗,x1会回答yes或者no,如果x1是一个坏人他会说谎话,如果它是好人,他会说真话。

如果没有确切的证据找出这p1和p2个人,那么就写no。否则就按大小顺序输出。

题解:
x 1 使 { x 2 使 y e s x 2 n o x1是天使\begin{cases} x2是天使&说yes\\ x2是恶魔&说no\\ \end{cases}

x 1 { x 2 y e s x 2 使 n o x1是恶魔\begin{cases} x2是恶魔&说yes\\ x2是天使&说no\\ \end{cases}

由上分析知道说yes时两个人同属性,说no时不同属性。

故而做成并查集,有相关性的人会被放到同个集合内,而集合内有2种属性,即是否和根节点相同属性。

这时候就转化成另一个问题了,有(p1+p2)个数,其中有p1个A和p2个B,每组数里面有2种属性的人,各知道人数,但不知道是A还是B。这个问题用背包可以解决。

#include "cstdio"
#include "cstring"
#include "vector"
#include "algorithm"
using namespace std;
const int maxn=609;
int n,p1,p2,fa[maxn],rak[maxn];
int anc(int a)
{
    if(fa[a]==a)return a;
    int k=fa[a];
    fa[a]=anc(fa[a]);
    rak[a]=(rak[k]+rak[a]+2)%2;
    return  fa[a];
}
void connect(int a,int b,int c)
{
    int fa_a=anc(a);                //tle 的原因:这里写成fa[a]了。
    int fa_b=anc(b);
    if(fa_a!=fa_b) {
        fa[fa_b] = fa_a;
        rak[fa_b]=(rak[a]-rak[b]+c+2)%2;
    }
}
void init(int n)
{
    for(int i=1;i<=n;++i)
    {
        fa[i]=i;
        rak[i]=0;

    }
}
int dp[maxn][maxn],step[maxn][maxn];
int main()
{
//    freopen("in.text","r",stdin);
    while (scanf("%d %d %d",&n,&p1,&p2))
    {
        if(n==0&&p1==0&&p2==0)break;
        init(p1+p2);
        //把元素都分好集合
        for(int i=1;i<=n;++i)
        {
            int a,b,c;
            char cc[4];
            scanf("%d %d %s",&a,&b,cc);
            if(cc[0]=='y')c=0;  else c=1;
            connect(a,b,c);
        }

        int cnt=0;  //集合数
        int party[maxn];  //记录每个集合的根节点
        int party_man[maxn][2];  //记录每个集合的人数
        //算清每个集合里的A和B的数量
        for(int i=1;i<=p1+p2;++i)
        {
            int d,fa_i=anc(i);
            for( d=1;d<=cnt;++d)
            {
                if(fa_i==party[d]){
                    party_man[d][rak[i]]++;
                    break;
                }
            }
            if(d==cnt+1)
            {
                party[++cnt]=fa_i;
                party_man[cnt][0]=0;
                party_man[cnt][1]=0;
                party_man[cnt][rak[i]]=1;
            }
        }
        //用背包的方法算清如何分配正确。

        memset(dp,0,sizeof(dp));
        memset(step,0,sizeof(step));  //记录步骤

        dp[0][0]=1;
        for(int d=0;d<=p1;++d)
            for(int i=1;i<=cnt;++i)
            {
                for(int k=0;k<=1;++k) {
                    int now = party_man[i][k];
                    if (d-now>=0&&dp[i - 1][d - now] > 0)
                    {
                        dp[i][d] += dp[i - 1][d-now];
                        step[i][d]=now;

                    }
                }
            }

        if(dp[cnt][p1]!=1){printf("no\n"); continue;}  //注意此处
        vector<int>finalans;
        int xx=cnt,yy=p1;
        for(int i=cnt;i>=1;--i)
        {
            int now=step[i][yy];  yy-=now;
            if(party_man[i][1]!=now&&party_man[i][0]!=now)continue;
            int aa=0;
            if(party_man[i][1]==now)aa=1;

            for(int d=1;d<=p1+p2;++d)                         
                //wa 的原因:这里p1+p2写成n了
            {
                if(anc(d)==party[i]&& rak[d]==aa)
                    finalans.push_back(d);
            }

        }

        sort(finalans.begin(),finalans.end());
        for(int i=0;i<finalans.size();++i)
            printf("%d\n",finalans[i]);
        printf("end\n");
    }
    return  0;
}
发布了145 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43235540/article/details/104130665