題目來源: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;
}