poj 1417 True Liars - 并查集 - 动态规划

题目传送门

  传送点I

  传送点II

题目大意

  有若干个人,每个人要么是好人要么是坏人。好人永远说真话,坏人永远说假话。它们一共说了$n$句话,每句话是形如$x$是坏人或者好人。问能否唯一确定好人集合。

  考虑2-sat。然后发现建图会发生一些有趣的事情

  如果A说了一句C,说是B是好人,那么可以建成下面这个图(以下两张图请自行把右下角的A'看成B'):

  如果A说了一句C,说是B是坏人,那么可以建成下面这个图:

  两个图中,连的边都是无向边,然后发现每句话不用建点,直接可以省去。每句话可以看成告诉你两个人是不是同一部落的。

  因为图太简单了,然后就不用2-sat了,直接上带权并查集维护点之间的相等和不等关系。

  然后可以得到若干个对立的联通块。如果一个块是好人块,那么对立的块就是坏人块。

  然后可以做一遍背包。这道题就水完了。

  当然,这道题完全可以不用并查集。直接建图,对于未被访问的点从它开始搜索,可以找出所有和它是同一部落的人,这个连通块剩下的就是在不同部落的人。

Code

 1 /**
 2  * poj
 3  * Problem#1417
 4  * Accepted
 5  * Time: 16ms
 6  * Memory: 1348k
 7  */
 8 #include <iostream>
 9 #include <cstdlib>
10 #include <cstring>
11 #include <cstdio>
12 using namespace std;
13 typedef bool boolean;
14 
15 const int N = 1005;
16 
17 int n, nperson;
18 int divine, devilish;
19 int uf[N], relation[N];
20 int f[N][305];
21 int size[N][2];
22 char str[10];
23 
24 int find(int x) {
25     if (uf[x] == x)
26         return x;
27     int next_father = find(uf[x]);
28     relation[x] ^= relation[uf[x]];
29     return uf[x] = next_father;
30 }
31 
32 inline boolean init() {
33     scanf("%d%d%d", &n, &divine, &devilish);
34     if (!n && !divine && !devilish)
35         return false;
36     nperson = divine + devilish;
37     for (int i = 1; i <= nperson; i++)
38         uf[i] = i, relation[i] = 0;
39     for (int i = 1, u, v; i <= n; i++) {
40         scanf("%d%d%s", &u, &v, str);
41         int newr = (str[0] == 'n');
42         if (find(u) != find(v)) {
43             relation[uf[u]] = relation[u] ^ newr;
44             uf[uf[u]] = v;
45         }
46     }
47     return true;
48 }
49 
50 inline void solve() {
51     for (int i = 1; i <= nperson; i++)
52         size[i][0] = size[i][1] = 0;    
53     for (int i = 1; i <= nperson; i++)
54         if (find(i) == i)
55             size[i][0]++;
56         else
57             size[find(i)][relation[i]]++;
58     int ls = 0;
59     memset(f, 0, sizeof(f));
60     f[0][0] = 1;
61     for (int i = 1; i <= nperson; i++)
62         if (find(i) == i) {
63             for (int j = size[i][0]; j <= divine; j++)
64                 f[i][j] = min(2, f[i][j] + f[ls][j - size[i][0]]);
65             for (int j = size[i][1]; j <= divine; j++)
66                 f[i][j] = min(2, f[i][j] + f[ls][j - size[i][1]]);
67             ls = i;
68         }
69 
70     if (f[ls][divine] != 1)
71         puts("no");
72     else {
73         int rest = divine;
74         for (int i = ls - 1; ~i; i--) {
75             if (find(i) == i) {
76                 if (size[ls][0] <= rest && f[i][rest - size[ls][0]])
77                     rest -= size[ls][0], size[ls][0] = -1;
78                 else
79                     rest -= size[ls][1], size[ls][1] = -1;
80                 ls = i;
81             }
82         }
83 
84         for (int i = 1; i <= nperson; i++)
85             if (size[find(i)][relation[i]] == -1)
86                 printf("%d\n", i);
87         puts("end");
88     }
89 }
90 
91 int main() {
92     while (init())
93         solve();
94     return 0;
95 }

猜你喜欢

转载自www.cnblogs.com/yyf0309/p/9424560.html