题目来源: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;
}