POJ 1417 True Liars 【种类并查集+背包问题】

题目来源:http://poj.org/problem?id=1417
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
★写了这题发现自己背包都不会,然后又去看了下背包…


题意:

在一个小岛上有两个种族,魔族和神族,你不能直接区分它们。已知神族部落的成员只会说实话,魔族部落的成员只会说假话。你问了它们n个问题(问x y是什么部落的),你知道神族有和魔族各有多少人,请你完全区分出哪些人是神族,哪些人是魔族。如果可以区分,输出神族的成员。


思路:

① 种类并查集

首先可以明白一点:
如果x 说 y是魔族:此时如果x是魔族,y就是神族;如果x是神族,那么y就是魔族 即x与y属于不同的种族
如果x 说 y是神族:此时如果x是神族,y就是神族;如果x是魔族,那么y就是魔族 即x与y属于相同的种族
令0表示相同的种族,1表示不同的种族 ~ 那么正好 (1+1)%2=0 表示不同的不同就是相同

也就是说我们可以用种类并查集来保存 他们种族直接的关系*(dad数组和val数组解决)*
现在我们把所有人分在了很多个集合(cnt个)中,对于每个集合又分为神族和魔族两个集合(我们不知道哪个是神哪个是魔,这只是一个相对关系)

② 背包求方案数

dp[ i ][ j ]表示前i个集合中神族有j人的方案数,可知如果最后dp[ cnt ][ p1 ]!=1 那么方案数不唯一或没有,输出no 否则就进入下一个环节

③ 背包求具体方案

在求方案数的过程中用pre[ i ][ j ]保存 dp[ i ][ j ]上一步的神族人数,然后从最后一个往第一个集合倒推就可以求出来了

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
using namespace std;
const int maxn=666+5;
const int mod=1e9+7;
const int inf=2e9;
const double eps=1e-8;
const double pi=acos(-1);
typedef long long LL;
int n,p1,p2;
int dad[maxn],val[maxn];
vector<int> v[maxn][2];
bool vis[maxn];
int sz[maxn][2];
int dp[maxn][maxn>>1];
int pre[maxn][maxn>>1];
int ans[maxn];
template<class t>
inline void read(t &x)
{
    char c; x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    t res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int seek(int k)
{
    if(dad[k]==-1) return k;
    int tmp=seek(dad[k]);
    val[k]=(val[k]+val[dad[k]])%2;
    return dad[k]=tmp;
}
int main()
{
    while(~scanf("%d%d%d",&n,&p1,&p2)){
        if(n==0&&p1==0&&p2==0) break;
        memset(dad,-1,sizeof dad);
        memset(val,0,sizeof val);
        char s[10];
        int a,b;
        for(int i=1;i<=n;i++){
            scanf("%d%d%s",&a,&b,s);
            int flag=(s[0]=='n');
            int fa=seek(a),fb=seek(b);
            if(fa!=fb){
                dad[fa]=fb;
                val[fa]=(val[fa]+val[b]+flag-val[a]+2)%2;
            }
        }
        for(int i=0;i<=p1+p2;i++){
            v[i][0].clear();
            v[i][1].clear();
            sz[i][0]=sz[i][1]=0;
        }
        memset(vis,0,sizeof vis);
        int cnt=1;
        for(int i=1;i<=p1+p2;i++){
            if(!vis[i]){
                int fi=seek(i);
                for(int j=i;j<=p1+p2;j++){
                    if(fi==seek(j)){
                        vis[j]=1;
                        v[cnt][val[j]].push_back(j);
                        sz[cnt][val[j]]++;
                    }
                }
                cnt++;
            }
        }
        cnt--;
//        for(int i=1;i<=cnt;i++){
//            cout<<sz[i][0]<<' '<<sz[i][1]<<endl;
//            for(int j=0;j<sz[i][1];j++) cout<<v[i][1][j]<<' ';
//            cout<<endl;
//        }
        memset(dp,0,sizeof dp);
        dp[0][0]=1;
        for(int i=1;i<=cnt;i++){
            for(int j=p1;j>=0;j--){
                if(j>=sz[i][0]&&dp[i-1][j-sz[i][0]]){
                    dp[i][j]+=dp[i-1][j-sz[i][0]];
                    pre[i][j]=j-sz[i][0];
                }
                if(j>=sz[i][1]&&dp[i-1][j-sz[i][1]]){
                    dp[i][j]+=dp[i-1][j-sz[i][1]];
                    pre[i][j]=j-sz[i][1];
                }
            }
        }
//        cout<<dp[cnt][p1]<<'x'<<endl;
        if(dp[cnt][p1]!=1){
            cout<<"no\n";
            continue;
        }
        ans[0]=0;
        int tot=p1;
        for(int i=cnt;i>=1;i--){
            int tmp=tot-pre[i][tot];
            if(tmp==sz[i][0]){
                for(int k=0;k<v[i][0].size();k++) ans[++ans[0]]=v[i][0][k];
            }
            else{
                for(int k=0;k<v[i][1].size();k++) ans[++ans[0]]=v[i][1][k];
            }
            tot=pre[i][tot];
        }
        sort(ans+1,ans+ans[0]+1);
        for(int i=1;i<=ans[0];i++) cout<<ans[i]<<endl;
        cout<<"end\n";
    }
    return 0;
}

发布了71 篇原创文章 · 获赞 89 · 访问量 8544

猜你喜欢

转载自blog.csdn.net/weixin_43890662/article/details/102683029