POJ 1417 True Liars
题意:有两种人,一种人只会说真话,另一种人只会说假话。只会说真话的人有p1个,另一种人有p2个。给出m个指令,每个指令为a b yes/no,意思是,如果为yes,a说b是只说真话的人,如果为no,a说b是只说假话的人。注意,a可以为b。保证每个指令都是正确的,且相互之间不矛盾。问,能不能确定哪些人是说真话的人,如果能,输出所有只说真话的人;如果不能,输出no。
解法:首先,用并查集建树,每个节点有两个参数,f和r,f表示父亲节点的编号,r表示与父亲节点是不是同一种人。r为0表示该节点与父亲节点是一种人,r为1表示该节点与父亲节点不是一种人。则用并查集处理之后,就可以分为k棵树,每棵树上可能有x[i]个人是同一种人,y[i]个是另一种人,(不确定哪个是说真话人的数量,哪个是说假话的人的数量),且每棵树之间互不影响。所以即是一个背包问题,用dp解决即可。
tag:并查集,DP,背包
1 /* 2 * Author: Plumrain 3 * Created Time: 2013-11-28 10:26 4 * File Name: DS-POJ-1417.cpp 5 */ 6 #include <iostream> 7 #include <cstdio> 8 #include <cstring> 9 #include <algorithm> 10 #include <vector> 11 #include <map> 12 13 using namespace std; 14 15 #define CLR(x) memset(x, 0, sizeof(x)) 16 #define CLR1(x) memset(x, -1, sizeof(x)) 17 #define PB push_back 18 typedef pair<int, int> pii; 19 20 struct oo{ 21 int a, b, pos; 22 void clr(){ 23 a = 0; b = 0; 24 } 25 }; 26 27 struct node{ 28 int f, r; 29 }; 30 31 node p[5005]; 32 pii num[5005]; 33 vector<int> ans; 34 oo cnt[5005]; 35 map<int, int> mp; 36 int n, m, p1, p2, d[1205][1205]; 37 38 int find(int x) 39 { 40 if (x != p[x].f){ 41 int y = p[x].f; 42 p[x].f = find(p[x].f); 43 p[x].r = (p[x].r + p[y].r) % 2; 44 } 45 return p[x].f; 46 } 47 48 void merge(int a, int b, int x, int t1, int t2) 49 { 50 p[t1].f = t2; 51 p[t1].r = (p[a].r + p[b].r + x) % 2; 52 } 53 54 void init() 55 { 56 for (int i = 0; i <= n; ++ i) 57 cnt[i].clr(); 58 59 for (int i = 0; i <= n; ++ i){ 60 p[i].f = i; 61 p[i].r = 0; 62 } 63 64 int a, b, x; 65 char s[10]; 66 for (int i = 0; i < m; ++ i){ 67 scanf ("%d%d%s", &a, &b, s); 68 if (s[0] == 'y') x = 0; 69 else x = 1; 70 71 int t1 = find(a), t2 = find(b); 72 if (t1 != t2) 73 merge(a, b, x, t1, t2); 74 } 75 } 76 77 void gao0() 78 { 79 if (p2 == 0 || p1 == 0){ 80 for (int i = 1; i <= p1; ++ i) 81 printf ("%d\n", i); 82 printf ("end\n"); 83 return; 84 } 85 printf ("no\n"); 86 } 87 88 int main() 89 { 90 while (scanf ("%d%d%d", &m, &p1, &p2) != EOF){ 91 if (!m && !p1 && !p2) break; 92 93 if (!m){ 94 gao0(); 95 continue; 96 } 97 98 n = p1 + p2; 99 init(); 100 101 if (p1 == p2){ 102 printf ("no\n"); 103 continue; 104 } 105 106 CLR (num); 107 for (int i = 1; i <= n; ++ i){ 108 int y = find(i); 109 if (!p[i].r) ++ num[y].first; 110 else ++ num[y].second; 111 } 112 113 int all = 1; 114 for (int i = 1; i <= n; ++ i) 115 if (num[i].first + num[i].second){ 116 cnt[all].a = num[i].first; 117 cnt[all].b = num[i].second; 118 cnt[all].pos = i; 119 ++ all; 120 } 121 122 CLR1 (d); 123 d[0][0] = 1; 124 for (int i = 1; i < all; ++ i) 125 for (int j = 0; j <= p1; ++ j){ 126 if (j >= cnt[i].a && d[i-1][j-cnt[i].a] >= 0){ 127 if (d[i][j] == -1) d[i][j] = 0; 128 d[i][j] += d[i-1][j-cnt[i].a]; 129 } 130 if (j >= cnt[i].b && d[i-1][j-cnt[i].b] >= 0){ 131 if (d[i][j] == -1) d[i][j] = 0; 132 d[i][j] += d[i-1][j-cnt[i].b]; 133 } 134 } 135 136 if (d[all-1][p1] != 1) printf ("no\n"); 137 else{ 138 int j = p1; 139 mp.clear(); 140 for (int i = all-1; i; -- i){ 141 if (j >= cnt[i].a && d[i-1][j-cnt[i].a] == 1){ 142 j -= cnt[i].a; 143 mp[cnt[i].pos] = 0; 144 } 145 else if (j >= cnt[i].b && d[i-1][j-cnt[i].b] == 1){ 146 j -= cnt[i].b; 147 mp[cnt[i].pos] = 1; 148 } 149 } 150 151 ans.clear(); 152 for (int i = 1; i <= n; ++ i){ 153 int y = find(i); 154 if (mp.count(y) && mp[y] == p[i].r) 155 ans.PB (i); 156 } 157 158 sort(ans.begin(), ans.end()); 159 int sz = ans.size(); 160 for (int i = 0; i < sz; ++ i) 161 printf ("%d\n", ans[i]); 162 printf ("end\n"); 163 } 164 165 } 166 return 0; 167 }
------------------------------------------------------------------
现在的你,在干什么呢?
你是不是还记得,你说你想成为岩哥那样的人。
现在的你,在干什么呢?
你是不是还记得,你说你想成为岩哥那样的人。