http://poj.org/problem?id=1417
可以发现 a说b是神 则ab同类 反之则异类 这样可以处理出每个连通块内神或魔各有多少个
然后dp[i][j]代表走完前i个连通块后且有j个神的情况有多少 顺带记录一下路径即可 因为要求答案唯一 直接输出路径即可
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define pb push_back
const int maxn=6e2+10;
vector <int> pre,ans;
int dp[maxn][maxn],path[maxn][maxn];
int f[maxn],dis[maxn],cnt[maxn][2];
int q,n1,n2;
int getf(int p)
{
int res;
if(f[p]==p) return p;
res=getf(f[p]);
dis[p]=(dis[p]+dis[f[p]])%2;
f[p]=res;
return res;
}
bool unite(int u,int v,int val)
{
int fu,fv;
fu=getf(u),fv=getf(v);
if(fu!=fv){
f[fv]=fu;
dis[fv]=(dis[u]+dis[v]+val)%2;
if(dis[fv]){
cnt[fu][0]+=cnt[fv][1];
cnt[fu][1]+=cnt[fv][0];
}
else{
cnt[fu][0]+=cnt[fv][0];
cnt[fu][1]+=cnt[fv][1];
}
return 1;
}
else{
if((dis[u]-dis[v]+2)%2!=val) return 0;
else return 1;
}
}
int main()
{
bool res,flag;
int i,j,k,u,v;
char ch[10];
while(scanf("%d%d%d",&q,&n1,&n2)!=EOF){
if(q==0&&n1==0&&n2==0) break;
for(i=1;i<=n1+n2;i++){
f[i]=i,dis[i]=0,cnt[i][0]=1,cnt[i][1]=0;
}
flag=1;
while(q--){
scanf("%d%d%s",&u,&v,ch);
if(!flag) continue;
if(ch[0]=='y') res=unite(u,v,0);
else res=unite(u,v,1);
if(!res) flag=0;
}
if(flag){
pre.clear();
pre.pb(0);
for(i=1;i<=n1+n2;i++){
if(f[i]==i){
pre.pb(i);
//printf("*%d %d %d*\n",i,cnt[i][0],cnt[i][1]);
}
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(i=1;i<=pre.size()-1;i++){
for(j=0;j<=n1;j++){
if(j-cnt[pre[i]][0]>=0&&dp[i-1][j-cnt[pre[i]][0]]!=0){
dp[i][j]+=dp[i-1][j-cnt[pre[i]][0]];
path[i][j]=0;
}
if(j-cnt[pre[i]][1]>=0&&dp[i-1][j-cnt[pre[i]][1]]!=0){
dp[i][j]+=dp[i-1][j-cnt[pre[i]][1]];
path[i][j]=1;
}
}
}
//printf("***%d***\n",dp[pre.size()-1][n1]);
if(dp[pre.size()-1][n1]==1){
ans.clear();
j=n1;
for(i=pre.size()-1;i>=1;i--){
for(k=1;k<=n1+n2;k++){
if(getf(k)==pre[i]&&dis[k]==path[i][j]){
ans.pb(k);
}
}
j-=cnt[pre[i]][path[i][j]];
}
sort(ans.begin(),ans.end());
for(i=0;i<ans.size();i++){
printf("%d\n",ans[i]);
}
printf("end\n");
}
else printf("no\n");
}
else printf("no\n");
}
return 0;
}