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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章