zoukankan      html  css  js  c++  java
  • poj1417菜鸡的详细题解(希望能帮助到和我一样陷于本题的新手)

    题意太麻烦就偷懒转载他人的题意。。。。。

    题意转载自http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove

    题目:给出p1+p2个人,其中p1个是好人,p2个是坏人。然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人。

    其中比较重要的是,好人总说真话,坏人总说假话。不需要判断矛盾。唯一解

    http://poj.org/problem?id=1417 

    其中好人说真话,坏人说假话这点很重要。

    那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。

    如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。

    也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。

    用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。这是一个经典的并查集问题。

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

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

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

    背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种,或者dp[i][j]表示当前好人i个,坏人j个的情况有多少种

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

    如果为1题目还需要输出方案,这点比较纠结。用后一种DP的时候WA了好多次,而这题又卡内存,不能开三维数组,其实可以两次DP解决。

    后来采用前者DP,不断从dp[cnt][p1]往前递推,递推的结果也必须是某个前趋状态的dp值为1.

    本代码改进了dp的空间大小,并由一c语言新手所编,其目的是为了帮助和我一样的新手理解和解决本题。

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    int f[1000],sex[1000];//0代表同类,1代表不同类;
    int sum[2][1000];//记录人数
    int cnt,mark[1000][1000];//记录取的是哪个小集合
    int w[2][1000],p[1000];//相当于背包问题中的提取出重量
    int dp[2][1000];
    int ans[1000];//记录答案
    int init(int n)
    {
    int i;
    memset(sex,0,sizeof(sex));
    for(i=1;i<=n;i++)
    {
    f[i]=i;
    sum[0][i]=1;//相同数
    sum[1][i]=0;//不同数
    }
    }
    int find(int x)
    {
    if(x==f[x])
    return x;
    int t=f[x];
    f[x]=find(f[x]);//找爷爷
    sex[x]=sex[t]^sex[x];//通过x与x父亲确定x与x爷爷的关系
    return f[x];
    }
    int Union(int x,int y,int k)
    {
    int fx=find(x);
    int fy=find(y);
    if(fx==fy)
    return 0;
    f[fy]=fx;
    sex[fy]=sex[x]^sex[y]^k;//更新fy与fx的关系
    sum[0][fx]+=sum[sex[fy]^0][fy];//将fy的人数并到fx中
    sum[1][fx]+=sum[sex[fy]^1][fy];
    return 0;
    }
    int main()
    {
    int n,p1,p2;
    while(scanf("%d%d%d",&n,&p1,&p2)!=EOF&&(n+p1+p2))
    {
    int i,j,k;
    int x,y;
    char a[5];
    int p0=p1+p2;
    init(p0);//初始数据
    for(i=0;i<n;i++)
    {
    scanf("%d%d%s",&x,&y,a);
    if(a[0]=='n')
    Union(x,y,1);
    else if(a[0]=='y')
    Union(x,y,0);
    }
    cnt=0;
    for(i=1;i<=p0;i++)//提取出各个大集合 (每个大集合里有两个小集合)
    {
    if(find(i)==i)
    {
    w[0][cnt]=sum[0][i];
    w[1][cnt]=sum[1][i];
    p[cnt]=i;
    cnt++;
    }
    }
    int b1,b2;
    b1=0;
    b2=1;
    memset(mark,0,sizeof(mark));
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;//最基本的一种情况
    for(i=0;i<cnt;i++)
    {
    int t;
    memset(dp[b2],0,sizeof(dp[b2]));//初始化下一种状态
    for(t=p1;t>=w[0][i];t--)//dp思想,求第i个集合时取t个人有几种情况 ,如果看不懂请先百度背包九讲理解01背包的解决思路。
    {

    if(dp[b1][t-w[0][i]])
    {
    dp[b2][t]+=dp[b1][t-w[0][i]];
    //printf("Wdp[%d][%d]=%d ",b2,t,dp[b2][t]);
    mark[i][t]=0;//记录取的是哪个小集合里的
    }
    }
    for(t=p1;t>=w[1][i];t--)
    {
    if(dp[b1][t-w[1][i]])
    {
    dp[b2][t]+=dp[b1][t-w[1][i]];
    //printf("Qdp[%d][%d]=%d ",b2,t,dp[b2][t]);
    mark[i][t]=1;
    }
    }
    b1=b1^1;
    b2=b2^1;
    }
    k=0;
    if(dp[cnt%2][p1]!=1)
    printf("no ");
    else
    {
    for(i=cnt-1;i>=0;i--)
    {
    int cur;
    cur=mark[i][p1];//看取的是i的哪个小集合
    for(j=1;j<=p0;j++)
    {
    if(find(j)==p[i]&&cur==sex[j])//找到i个大集合的元素并判断是哪个小集合
    {
    ans[k++]=j;
    }
    }
    if(cur==0)//更新p1以便找寻是下一个大集合的小集合
    p1-=w[0][i];
    else
    p1-=w[1][i];
    }
    sort(ans,ans+k);
    for(i=0;i<k;i++)
    printf("%d ",ans[i]);
    printf("end ");
    }
    }
    return 0;
    }

  • 相关阅读:
    JQuery是继prototype之后又一个优秀的Javascript库
    IAsyncResult接口
    Asynchronous Programming Patterns
    操作数据库的时候,使用自带的DbProviderFactory类 (涉及抽象工厂和工厂方法)
    8.2.4对象之间的关系
    git squash 和 git rebase
    8.2.3多态性 第8章 面向对象编程简介
    github的使用教程
    第7章 调试和错误处理 7.1.1 VS中的调试
    markdown的语法说明
  • 原文地址:https://www.cnblogs.com/cglongge/p/8530829.html
Copyright © 2011-2022 走看看