题意:
一个岛上存在着两种居民,一种是天神,一种是恶魔。
天神永远都不会说假话,而恶魔永远都不会说真话。
岛上的每一个成员都有一个整数编号(类似于身份证号,用以区分每个成员)。
现在你拥有n次提问的机会,但是问题的内容只能是向其中一个居民询问另一个居民是否是天神,请你根据收集的回答判断各个居民的身份。
题解:
不难发现,如果输入 a b yes 则表示 a 与 b 在同一个种族, a b no 则表示 a 与 b 在不同的种族。
先用带权并查集处理出各点之间的关系,在一个集合中的点即为确定关系的点,可以分为与根节点距离为 1 的点和距离为 0 的点。距离为 0 的点即为与根节点同种族的点,反之则为不同种族的点。
处理出每个集合之后,问题则转化为若干组物品,每组有两个,问最终能够凑成重量为 p1 的方法数。如果方法数唯一,则表示有解。
可以用 dp 解决。然后记录路径即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define int ll
#define PI acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<int, int>
#define fastio ios::sync_with_stdio(false), cin.tie(0)
const int mod = 998244353;
const int M = 1000000 + 10;
const int N = 1000 + 10;
int n, a, b;
int f[N], d[N];
int dp[N][N];
int get(int x)
{
if(f[x] == x) return x;
int rt = get(f[x]);
d[x] = (d[x] + d[f[x]]) % 2;
return f[x] = rt;
}
signed main()
{
while(cin >> n >> a >> b, n || a || b) {
memset(dp, 0, sizeof dp);
int tot = a + b;
for(int i = 1; i <= tot; i ++) f[i] = i, d[i] = 0;
for(int i = 1; i <= n; i ++) {
int x, y; string s;
cin >> x >> y >> s;
int rx = get(x), ry = get(y);
if(s[0] == 'y') {
f[rx] = ry;
d[rx] = (d[y] - d[x] + 2) % 2;
} else {
f[rx] = ry;
d[rx] = (d[y] - d[x] + 1 + 2) % 2;
}
}
int cnt = 0, rtid[N], num[N][2] = {0};
for(int i = 1; i <= tot; i ++) {
int ri = get(i);
if(ri == i) rtid[ri] = ++cnt;
}
for(int i = 1; i <= tot; i ++) {
int ri = get(i);
num[rtid[ri]][d[i]] ++;
}
dp[0][0] = 1;
for(int i = 1; i <= cnt; i ++) {
for(int j = a; j >= num[i][0]; j --) {
if(dp[i - 1][j - num[i][0]]) {
dp[i][j] += dp[i - 1][j - num[i][0]];
}
}
for(int j = a; j >= num[i][1]; j --) {
if(dp[i - 1][j - num[i][1]]) {
dp[i][j] += dp[i - 1][j - num[i][1]];
}
}
}
if(dp[cnt][a] != 1) {
cout << "no" << endl;
} else {
bool ans[N][2] = {false};
for(int i = cnt, j = a; i >= 1; i --) {
if(dp[i - 1][j - num[i][0]] == 1) {
ans[i][0] = true;
j -= num[i][0];
}
else if(dp[i - 1][j - num[i][1]] == 1) {
ans[i][1] = true;
j -= num[i][1];
}
}
for(int i = 1; i <= tot; i ++) {
if(ans[rtid[get(i)]][d[i]]) cout << i << endl;
}
cout << "end" << endl;
}
}
return 0;
}
/*
Rejoicing in hope, patient in tribulation.
7 4 2
2 3 no
3 4 no
4 3 no
5 5 yes
5 3 no
5 5 yes
1 5 yes
1
2
4
5
end
0 3 0
0 0 3
0 10 20
2 10 0
1 2 yes
2 2 yes
2 0 10
1 2 yes
2 2 yes
2 5 5
1 2 yes
2 2 yes
1
2
3
end
end
no
1
2
3
4
5
6
7
8
9
10
end
end
no
*/