标签:ret coff 描述 math while org nbsp 推荐 space
[//]: # (推荐题解模板,请替换blablabla等内容 ^^)
### 题目描述
一个岛上存在着两种居民,一种是天神,一种是恶魔。
天神永远都不会说假话,而恶魔永远都不会说真话。
岛上的每一个成员都有一个整数编号(类似于身份证号,用以区分每个成员)。
现在你拥有n次提问的机会,但是问题的内容只能是向其中一个居民询问另一个居民是否是天神,请你根据收集的回答判断各个居民的身份。
输入包含多组测试用例。
每组测试用例的第一行包含三个非负整数n,p1,p2p1,p2,其中n是你可以提问的总次数,p1p1是天神的总数量,p2p2是恶魔的总数量。
接下来n行每行包含两个整数xi,yixi,yi以及一个字符串aiai,其中xi,yixi,yi是岛上居民的编号,你将向编号为xixi的居民询问编号为yiyi的居民是否是天神,
aiai是他的回答,如果aiai为“yes”,表示他回答你“是”,如果aiai为“no”,表示他回答你“不是”。
xi,yixi,yi可能相同,表示你问的是那个人自己是否为天神。
当输入为占据一行的“0 0 0”时,表示输入终止。
对于每组测试用例,如果询问得到的信息足以使你判断每个居民的身份,则将所有天神的编号输出,每个编号占一行,在输出结束后,在另起一行输出“end”,表示该用例输出结束。
如果得到的信息不足以判断每个居民的身份,则输出“no”,输出同样占一行。
1≤xi,yi≤q1+q21≤xi,yi≤q1+q2,
1≤n<1000,1≤p1,p2<300
#### 样例
```
2 1 1
1 2 no
2 1 no
3 2 1
1 1 yes
2 2 yes
3 3 yes
2 2 1
1 2 yes
2 3 no
5 4 3
1 2 yes
1 3 no
4 5 yes
5 6 yes
6 7 no
0 0 0
no
no
1
2
end
3
4
5
6
end
```
----------
### 算法1
##### (并查集+dp+路径还原)
前置知识 并查集(食物链 那道题要会做),dp的路径还原(可以状态回溯)
1.对于 a b yes, 我们合并(a,b)和(a + p1 + p2, b + p1 + p2)
代表a,b一体,如果a是好人则b是好人,如果a(a + p1 + p2)是坏人,则b(b + p1 + p2)是坏人
2.对于 a b no, 合并(a, b + p1 + p2), (a + p1 + p2, b)含义和上面一样
仅有人之间的指认关系是不知道答案的,我们要找几个集合,恰好且仅有一种方法能凑出 p1 人才有答案
对于每个人,要么是好人,要么是坏人(废话,下面是关键)
所以对于每个人,
1.这个人 i 所在的集合全是好人,与 i + p1 + p2 处于一个集合的都是坏人
即father[其他人] == father[i] 的 都是好人, father[其他人] == father[i + p1 + p2] 的 都是坏人
2. 这个人 i 是坏人,………………
每个大集合分为,这个集合的father是好人, father是坏人
所以dp的时候必须从上个状态转移(不存在这个人及不是好人也不是坏人在状态转换)不能直接继承
对于前i个集合可拼成 j个好人, 转移:
dp[i][j] += dp[i - 1][j - 集合的father是好人时好人人数]
dp[i][j] += dp[i - 1][j - 集合的father是坏人时好人人数]
dp[i][j] 存的是能满足前i个集合可拼成 j个好人的方法数,只有当(dp[集合个数][p1] == 1)能唯一确定谁是好人,谁是坏人
dp状态的回溯见代码,毕竟 (dp[集合个数][p1] == 1) 路径是唯一的很简单
#### C++ 代码
```
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int f[1205], s[1205], n, x, y, sum, h[1205]; 5 int tot, a[605], vis[1205], b[605], dp[605][605]; 6 char str[5]; 7 8 int find(int x) 9 { 10 if (x == f[x]) return x; 11 return f[x] = find(f[x]); 12 } 13 14 void unit(int x, int y) 15 { 16 x = find(x), y = find(y); 17 if (x == y) return; 18 if (h[x] > h[y]) s[x] += s[y], f[y] = x; 19 else if (h[x] < h[y]) s[y] += s[x], f[x] = y; 20 else s[x] += s[y], f[y] = x, ++ h[x]; 21 } 22 23 int main() 24 { 25 while(scanf("%d%d%d", &n, &x, &y), n + x + y) 26 { 27 memset(dp, 0, sizeof dp); 28 sum = x + y, tot = 0, dp[0][0] = 1; 29 for (int i = 1; i<= sum; ++ i) 30 f[i] = i, f[i + sum] = i + sum, s[i] = h[i] = h[i + sum] = 1, s[i + sum] = vis[i] = vis[i + sum] = 0; 31 for (int i = 1, a, b; i <= n; ++ i) 32 { 33 scanf("%d%d%s", &a, &b, str + 1); 34 if (str[1] == ‘y‘) unit(a, b), unit(a + sum, b + sum); 35 else unit(a, b + sum), unit(a + sum, b); 36 } 37 for (int i = 1, fi; i<= sum; ++ i) 38 { 39 if((fi = find(i)) != i) continue; 40 vis[fi] = vis[fi + sum] = ++ tot; 41 a[tot] = s[fi], b[tot] = s[fi + sum]; 42 } 43 for (int i = 1; i <= tot; ++ i) 44 { 45 for (int j = min(a[i], b[i]); j <= x; ++ j) 46 { 47 if (j >= a[i]) dp[i][j] += dp[i - 1][j - a[i]]; 48 if (j >= b[i]) dp[i][j] += dp[i - 1][j - b[i]]; 49 } 50 } 51 if (dp[tot][x] != 1) {puts("no"); continue;} 52 for (int i = tot; i; -- i) 53 if (dp[i - 1][x - a[i]]) x -= a[i], a[i] = -1; 54 else if (dp[i - 1][x - b[i]]) x -= b[i], a[i] = -2; 55 for (int i = 1, fi = find(1); i <= sum; fi = find(++ i)) 56 if(vis[fi]) 57 if (fi > sum && a[vis[fi]] == -2) printf("%d\n", i); 58 else if (fi <= sum && a[vis[fi]] == -1) printf("%d\n", i); 59 puts("end"); 60 } 61 return 0; 62 }
```
----------
标签:ret coff 描述 math while org nbsp 推荐 space
原文地址:https://www.cnblogs.com/2aptx4869/p/12400567.html