题意:
有 p 个好人和 q 个坏人,好人永远说真话,坏人永远说假话。现给出一组话,问能否唯一确定每个人是好人还是坏人。
题解:
- 当 a 说 b 是好人时,如果 a 是好人,那么 b 也应该是好人,如果 a 是坏人,那么 b 也应该是坏人
- 当 a 说 b 是坏人时,如果 a 是好人,那么 b 就应该是坏人,如果 a 是坏人,那么 b 就应该是好人
那么输入的 opt = "yes"的时候,a 和 b 就应该属于同一个阵营的,否则 a 和 b 应该不属于同一阵营,用并查集维护每个集合中的人即可,但是我们还不知道每个集合中的人到底是好人还是坏人。
我们可以类比背包问题,要么选集合中的好人,要么选集合中的坏人,最后判断得到的好人 = p的方案数是否等于 1 即可,因为答案是唯一的,所以直接跑一次背包,记录之前有多少个好人,现在的好人数量,反向输出方案即可。
代码:
/*
* @Author : Nightmare
*/
#include <iostream>
#include <time.h>
#include <vector>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
#define ull unsigned long long
#define ls 2 * rt
#define rs 2 * rt + 1
#define gcd(a,b) __gcd(a,b)
#define eps 1e-8
#define lowbit(x) (x&(-x))
#define N 605
#define M 305
#define mod 1000000007
#define inf 0x3f3f3f3f
int n, p, q;
int fa[N], val[N], num[N][2], vis[N], dp[N][M], pre[N][M];
// num : 第 i 个集合中好人和坏人的个数
// dp : 前 i 个集合中好人为 j 个的方案数
// pre : 前 i 个集合中好人为 j 个的上一个状态有多少个好人
vector<int> b[N][2];
int find(int x){
if(fa[x] == x) return x;
int f = find(fa[x]);
val[x] = (val[x] + val[fa[x]]) % 2;
return fa[x] = f;
}
void init(){
for(int i = 0 ; i < N ; i ++){
fa[i] = i; val[i] = vis[i] = 0;
b[i][0].clear(); b[i][1].clear();
num[i][0] = num[i][1] = 0;
}
}
void solve(){
init();
for(int i = 1 ; i <= n ; i ++){
int a, b; string opt; cin >> a >> b >> opt;
int flag = (opt == "no");
int x = find(a), y = find(b);
if(x != y){
fa[x] = y;
val[x] = (val[b] - val[a] + flag + 2) % 2;
}
}
int cnt = 1;
for(int i = 1 ; i <= p + q ; i ++){
if(!vis[i]){
for(int j = i ; j <= p + q ; j ++){
if(find(i) == find(j)){
vis[j] = 1;
b[cnt][val[j]].push_back(j);
num[cnt][val[j]] ++;
}
}
cnt ++;
}
}
memset(dp, 0, sizeof(dp)); dp[0][0] = 1;
for(int i = 1 ; i < cnt ; i ++){
for(int j = p ; j >= 0 ; j --){
if(j >= num[i][0] && dp[i - 1][j - num[i][0]]){
dp[i][j] += dp[i - 1][j - num[i][0]];
pre[i][j] = j - num[i][0];
}
if(j >= num[i][1] && dp[i - 1][j - num[i][1]]){
dp[i][j] += dp[i - 1][j - num[i][1]];
pre[i][j] = j - num[i][1];
}
}
}
if(dp[cnt - 1][p] != 1){ puts("no"); return ; } // 方案数不唯一 或者 无解
vector<int> ans; int cur = p;
for(int i = cnt - 1 ; i >= 1 ; i --){
if(cur - pre[i][cur] == num[i][0]){
for(int j = 0 ; j < num[i][0] ; j ++)
ans.push_back(b[i][0][j]);
}else{
for(int j = 0 ; j < num[i][1] ; j ++)
ans.push_back(b[i][1][j]);
}
cur = pre[i][cur];
}
sort(ans.begin(), ans.end());
for(int i = 0 ; i < ans.size() ; i ++) printf("%d\n", ans[i]);
printf("end\n");
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("F:\\in.txt", "r", stdin);
#endif
while(~scanf("%d %d %d", &n, &p, &q) && (n + p + q)) solve();
#ifndef ONLINE_JUDGE
cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
return 0;
}