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 }
View Code

 

转载于:https://www.cnblogs.com/plumrain/p/POJ_1417.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值