【POJ1417】【带标记并查集+DP】True Liars

Description

After having drifted about in a small boat for a couple of days, Akira Crusoe Maeda was finally cast ashore on a foggy island. Though he was exhausted and despaired, he was still fortunate to remember a legend of the foggy island, which he had heard from patriarchs in his childhood. This must be the island in the legend. In the legend, two tribes have inhabited the island, one is divine and the other is devilish, once members of the divine tribe bless you, your future is bright and promising, and your soul will eventually go to Heaven, in contrast, once members of the devilish tribe curse you, your future is bleak and hopeless, and your soul will eventually fall down to Hell.

In order to prevent the worst-case scenario, Akira should distinguish the devilish from the divine. But how? They looked exactly alike and he could not distinguish one from the other solely by their appearances. He still had his last hope, however. The members of the divine tribe are truth-tellers, that is, they always tell the truth and those of the devilish tribe are liars, that is, they always tell a lie.

He asked some of them whether or not some are divine. They knew one another very much and always responded to him "faithfully" according to their individual natures (i.e., they always tell the truth or always a lie). He did not dare to ask any other forms of questions, since the legend says that a devilish member would curse a person forever when he did not like the question. He had another piece of useful informationf the legend tells the populations of both tribes. These numbers in the legend are trustworthy since everyone living on this island is immortal and none have ever been born at least these millennia.

You are a good computer programmer and so requested to help Akira by writing a program that classifies the inhabitants according to their answers to his inquiries.

Input

The input consists of multiple data sets, each in the following format :

n p1 p2 
xl yl a1 
x2 y2 a2 
... 
xi yi ai 
... 
xn yn an

The first line has three non-negative integers n, p1, and p2. n is the number of questions Akira asked. pl and p2 are the populations of the divine and devilish tribes, respectively, in the legend. Each of the following n lines has two integers xi, yi and one word ai. xi and yi are the identification numbers of inhabitants, each of which is between 1 and p1 + p2, inclusive. ai is either yes, if the inhabitant xi said that the inhabitant yi was a member of the divine tribe, or no, otherwise. Note that xi and yi can be the same number since "are you a member of the divine tribe?" is a valid question. Note also that two lines may have the same x‘s and y‘s since Akira was very upset and might have asked the same question to the same one more than once.

You may assume that n is less than 1000 and that p1 and p2 are less than 300. A line with three zeros, i.e., 0 0 0, represents the end of the input. You can assume that each data set is consistent and no contradictory answers are included.

Output

For each data set, if it includes sufficient information to classify all the inhabitants, print the identification numbers of all the divine ones in ascending order, one in a line. In addition, following the output numbers, print end in a line. Otherwise, i.e., if a given data set does not include sufficient information to identify all the divine members, print no in a line.

Sample Input

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

Sample Output

no
no
1
2
end
3
4
5
6
end

Source

Japan 2002 Kanazawa

【分析】

比较简单的题目,前面先用类似食物链的方法得到一些并查集集合,然后将每个并查集里面的与根在同一个集合中和不再同一个集合中的统计出来。

然后就就变成在每个并查集中从两个数中选一个数成为特定的值,很简单的DP。

最后输出方案简单标记一下,逆推即可。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cstring>
  5 #include <vector>
  6 #include <utility>
  7 #include <iomanip>
  8 #include <string>
  9 #include <cmath>
 10 #include <queue>
 11 #include <map>
 12 #define LOCAL
 13 const int MAXN = 1000 + 10;
 14 const int MAX = 30000 + 10;
 15 using namespace std;
 16 struct DATA{
 17        int a, b;
 18 }data[MAXN];
 19 int n, p1, p2, q;
 20 int parent[MAXN] ,val[MAXN];
 21 int num[MAXN];//num[i]代表如果i是并查集中的根,那么他在AB表中的位置就num[i]
 22 int f[MAXN][MAXN], cnt;
 23 int pre[MAXN][MAXN];//DP中记录状态转移过来的位置
 24 int Ans[MAXN];//表示答案中被选为好人或者不是好人
 25
 26 int find(int x){
 27     int f = x, tmp = 0;
 28     while (x != parent[x]){
 29           tmp += val[x];
 30           x = parent[x];
 31     }
 32     parent[f] = x;
 33     val[f] = tmp % 2;
 34     return x;
 35 }
 36
 37 void init(){
 38      n = p1 + p2;//即总的人数
 39      //val为0代表相同集合,1代表不同的集合
 40      for (int i = 1; i <= n; i++){
 41          val[i] = 0;
 42          parent[i] = i;
 43      }
 44      for (int i = 1; i <= q; i++){
 45          int x, y;
 46          char str[5];
 47          scanf("%d%d", &x, &y);
 48          scanf("%s", str);
 49          if (find(x) == find(y)) continue;//在同一个集合之内就不用考虑了
 50          else{
 51               if (str[0] == ‘y‘){ //一定是一种集合之内
 52                  int xx = find(x), yy = find(y);
 53                  parent[xx] = y;
 54                  val[xx] = (-val[x] + 2) % 2;
 55               }else{
 56                  int xx = find(x), yy = find(y);
 57                  parent[xx] = y;
 58                  val[xx] = (-val[x] + 3) % 2;
 59               }
 60          }
 61      }
 62 }
 63 //计算出各个并查集的同类和不同类
 64 void prepare(){
 65      memset(data, 0, sizeof(data));
 66      cnt = 0;
 67      for (int i = 1; i <= n; i++){
 68          if (parent[i] != i) continue;
 69          num[i] = ++cnt;
 70      }
 71      for (int i = 1; i <= n; i++){
 72          int xx = find(i);
 73          if (val[i] == 0) data[num[xx]].a++;//和它同一类
 74          else data[num[xx]].b++;//不同类
 75      }
 76 }
 77 void dp(){
 78      memset(f, 0, sizeof(f));
 79      //f[i][j]表示到了第i个好人有j个的时候的方案数量,注意只要保存3个量就可以了
 80      f[0][0] = 1;
 81      for (int i = 1; i <= cnt; i++)
 82      for (int j = p1; j >=0; j--){
 83          if (j - data[1].a >= 0) f[i][j] += f[i - 1][j - data[i].a];
 84          if (j - data[1].b >= 0) f[i][j] += f[i - 1][j - data[i].b];
 85          if (f[i][j] >= 2)  f[i][j] = 2;//不要做太大了
 86          if (f[i][j] == 1){//有解
 87             if (j - data[i].a >= 0 && f[i - 1][j - data[i].a] == 1) pre[i][j] = 0;//a类选为好人
 88             if (j - data[i].b >= 0 && f[i - 1][j - data[i].b] == 1) pre[i][j] = 1;//b类选为好人
 89          }
 90      }
 91      if (f[cnt][p1] != 1){printf("no\n");return;}
 92      int last = p1;
 93      for (int i = cnt; i >= 1; i--){
 94          Ans[i] = pre[i][last];
 95          if (Ans[i] == 0) last -= data[i].a;
 96          else last -= data[i].b;
 97      }
 98      for (int i = 1; i <= n; i++){
 99          int xx = find(i);
100          if (Ans[num[xx]] == 1 && val[i] == 1) printf("%d\n", i);
101          if (Ans[num[xx]] == 0 && val[i] == 0) printf("%d\n", i);
102      }
103      printf("end\n");
104 }
105
106 int main(){
107     int T;
108     #ifdef LOCAL
109     freopen("data.txt",  "r",  stdin);
110     freopen("out.txt",  "w",  stdout);
111     #endif
112     while (scanf("%d%d%d", &q, &p1, &p2)){
113           if (q == 0 && p1 == 0 && p2 == 0) break;
114           init();
115           prepare();
116           dp();
117           //printf("%d", data[1].b);
118     }
119     return 0;
120 }

时间: 2024-10-09 20:39:19

【POJ1417】【带标记并查集+DP】True Liars的相关文章

poj1417(种类并查集+dp)

题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示x说y是天使,ch为no表示x说y不是天使(x, y为天使,恶魔的编号,1<=x,y<=p+q):天使只说真话,恶魔只说假话: 如果不能确定所有天使的编号,输出no,若能确定,输出所有天使的编号,并且以end结尾: 注意:可能会有连续两行一样的输入:还有,若x==y,x为天使: 思路:种类并查集+

真正的骗子(并查集+dp+dp状态回溯)

[//]: # (推荐题解模板,请替换blablabla等内容 ^^) ### 题目描述 一个岛上存在着两种居民,一种是天神,一种是恶魔. 天神永远都不会说假话,而恶魔永远都不会说真话. 岛上的每一个成员都有一个整数编号(类似于身份证号,用以区分每个成员). 现在你拥有n次提问的机会,但是问题的内容只能是向其中一个居民询问另一个居民是否是天神,请你根据收集的回答判断各个居民的身份. 输入格式 输入包含多组测试用例. 每组测试用例的第一行包含三个非负整数n,p1,p2p1,p2,其中n是你可以提问

poj1417 true liars(并查集 + DP)详解

这个题做了两天了.首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索.总是超时. 后来看别人的结题报告, 才恍然大悟判断唯一得用DP. 题目大意: 一共有p1+p2个人,分成两组,一组p1个,一组p2个.给出N个条件,格式如下: x y yes表示x和y分到同一组 x y no表示x和y分到不同组 问分组情况是否唯一,若唯一则按从小到大顺序输出,否则输出no.保证不存在矛盾条件,但是有可能出现x=y的情况. 题目分析: 题中会给我们一些信息, 告诉我们那些是同一类, 哪些是不同类.

POJ 1417 True Liars(并查集+DP)

大意: 一个岛上有神与恶魔两个种族,神会说真话,恶魔会说假话.已知神与恶魔的个数,但不知道具体个人是属于哪个. n,x,y 这个人问n次 ,x为神的个数,y为恶魔的个数. 每次的问题为 xi,yi,a 问xi ,yi是否为神? a为yes/no.注意xi,yi可能为同一个人 若最终可得出哪些是神则从小到大输出神的编号,并最终输出end 否则输出no 思路: 经过简单推理可得,只要是说yes,xi,yi为同一个族,no则不为同一个族. 这样通过使用并查集+relation(relation[i]为

poj 1417 并查集+dp

转自:点我 题目:给出p1+p2个人,其中p1个是好人,p2个是坏人.然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人. 其中比较重要的是,好人总说真话,坏人总说假话.不需要判断矛盾.唯一解 http://poj.org/problem?id=1417 其中好人说真话,坏人说假话这点很重要. 那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人. 如果一个人说另一个人是坏人

[luogu P2170] 选学霸(并查集+dp)

题目传送门:https://www.luogu.org/problem/show?pid=2170 题目描述 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议.所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的M尽可能接近 输入输出格式 输入格式: 第一行,三个正整数N,M,K. 第2...K行,每行2个数,表示一对实力相当的人的编号(编号为1-N) 输出格式: 一行,表示既不让同学们抗议,又与原来的M尽可能接

hdu 2144 Evolution(并查集+dp)

先预处理一下数据..类似求LCS 然后并查集 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<queue> 7 #include<algorithm> 8 #include<map> 9 #include<iomanip> 10

[Tarjan][并查集][dp] Jzoj P4253 QYQ在艾泽拉斯

Description 在艾泽拉斯的无尽之海里,有着一群不为人知的由各个种族的冒险者统治的岛屿,这些岛屿都很庞大,足以在上面建造许多的城市,城市之间有一些单向道路连接.有一天,QYQ无意中发现了这些岛屿,并且发现在每个城市的地下都或多或少埋藏着一些装备.金币.宝物……可是正当QYQ兴奋不已打算全部把它们拿走时,他却惊奇的发现你的魔法在这里被限制住了,唯一可用的技能就是闪现,而且魔法只够他使用K次这个技能了,每次使用这个技能QYQ只能从一个岛屿上闪现到另外一个岛屿上.每一个岛屿只能登上一次,QYQ

【POJ1733】【带标记并查集】Parity game

Description Now and then you play the following game with your friend. Your friend writes down a sequence consisting of zeroes and ones. You choose a continuous subsequence (for example the subsequence from the third to the fifth digit inclusively) a