这个题做了两天了。首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索。总是超时。 后来看别人的结题报告, 才恍然大悟判断唯一得用DP.

题目大意:

一共有p1+p2个人,分成两组,一组p1个,一组p2个。给出N个条件,格式如下:

x y yes表示x和y分到同一组

x y no表示x和y分到不同组

问分组情况是否唯一,若唯一则按从小到大顺序输出,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。

题目分析: 题中会给我们一些信息, 告诉我们那些是同一类, 哪些是不同类。 当然刚开始的时候我们无法判断那一类是好人、坏人。 那么我们不妨把有关系的点(无论他们的关系是yes还是 no)全归为一类, 他们有一个相同的父节点。然后用一个数组(relation[])记录他与父节点的关系(0代表同类, 1代表异类)。当然因为所给的只是一部分信息, 所以有可能无法把所有点归为一类(例如:1,2 yes 3,4 yes。只表明1,2同类 , 3,4同类, 1,3的关系并不知道)。那么不妨设几个不同的集合(以父节点为划分标准)每个集合分为两类 ,与父节点同类(relation[] = 0), 与父节点不同类(relation[] = 1)。此时我们问题转变为答案是否唯一。取每个集合中的一种类型,且仅取一种(同类或不同类)。看累计人数得p1的情况是否唯一。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<string.h>
  4. using namespace std;
  5.  
  6. int n, n1, n2, mi, mx, key, flag, a[][], vis[][][], ans[][], v[], d[][], pre[], relation[];
  7. int find(int x)//寻找最根部的父亲节点
  8. {
  9. if(pre[x] == x)
  10. return x;
  11. else if(pre[x] != x)
  12. {
  13. int t = pre[x];
  14. pre[x] = find(pre[x]);//边寻找最根部父亲节点,边更新原父亲节点的信息
  15. relation[x] = (relation[t] + relation[x]) % ;//类似于种类并查集的更新
  16. }
  17. return pre[x];
  18. }
  19. void work(int a, int b, int c)//合并节点
  20. {
  21. int fx = find(a);
  22. int fy = find(b);
  23. if(fx != fy)
  24. {
  25. pre[fx] = fy;
  26. relation[fx] = (relation[a] + relation[b] + c) % ;
  27. }
  28. }
  29.  
  30. void dp()
  31. {
  32. int k = ;
  33. for(int i = mi+; i <= (n1+n2); i++)
  34. {
  35. if(v[i] == )
  36. {
  37. k++;
  38. int t1 = ans[i][];
  39. int t2 = ans[i][];
  40. int mx = min(t1, t2);
  41. for(int j = n1; j >= mx; j--)
  42. {
  43. d[k][j] = d[k-][j-t1] + d[k-][j-t2];
  44. if(d[k-][j-t1] == && d[k-][j-t2] == )
  45. {
  46. vis[k][j][] = i;
  47. vis[k][j][] = ;
  48. }
  49. else if(d[k-][j-t2] == && d[k-][j-t1] == )
  50. {
  51. vis[k][j][] = i;
  52. vis[k][j][] = ;
  53. }
  54. }
  55. }
  56. }
  57. }
  58. int main()
  59. {
  60. while(scanf("%d%d%d", &n, &n1, &n2) != EOF)
  61. {
  62. if(n == && n1 == && n2 == )
  63. break;
  64. //初始化所有节点得父节点和relation
  65. for(int i = ; i <= n1+n2; i++){pre[i] = i; relation[i] = ;}
  66. for(int i = ; i <= n; i++)
  67. {
  68. int a, b;
  69. char c[];
  70. scanf("%d%d%s", &a, &b, &c);
  71. if(strcmp(c, "yes") == )
  72. work(a, b, );
  73. else if(strcmp(c, "no") == )
  74. work(a, b, );
  75. }
  76. //这里注意一下:到这一步,有可能存在一些点的父节点不是最跟的节点
  77. for(int i = ; i <= n1+n2; i++)
  78. int t = find(i);
  79. memset(v, , sizeof(v));
  80. //ans[i][0]表示与最根节点i同类的节点个数, ans[i][1]代表与最根节点i不同类的节点个数
  81. memset(ans, , sizeof(ans));
  82. flag = ;//存储一共有多少个不同的最跟部的父节点
  83. mi = 10e9;
  84. for(int i = ; i <= n1+n2; i++)
  85. {
  86. int x = pre[i];
  87. int y = relation[i];
  88. if(x < mi)
  89. mi = x;
  90. ans[x][y]++;
  91. if(v[x] == )
  92. {
  93. v[x] = ;
  94. flag++;
  95. }
  96. }
  97. /*d[i][j]前i个集合中累计人数为j时有多少种可能, vis[i][j][]保存每一个集合选了哪一类, 为最后输出用。 vis[i][j][0]表示前i个集合中累计人数为j时的最根节点,vis[i][j][1] 表示前i个集合中累计人数为j时,最根节点为vis[i][j][0]时,与根节点的关系*/
  98. memset(d, , sizeof(d));
  99. memset(vis, , sizeof(vis));
  100. d[][ans[mi][]]++; d[][ans[mi][]]++;
  101. vis[][ans[mi][]][] = mi;
  102. vis[][ans[mi][]][] = ;
  103. vis[][ans[mi][]][] = mi;
  104. vis[][ans[mi][]][] = ;
  105. dp();
  106. if(d[flag][n1] == )//如果前flag个集合中累计人数为n1的可能为1时,有唯一解
  107. {
  108. int j = n1;
  109. for(int i = flag; i >= ; i--)//从后往前推
  110. {
  111. a[i][] = vis[i][j][];
  112. a[i][] = vis[i][j][];//记录第i个集合中取得是哪一类(同类0, 不同类1)
  113. j -= ans[a[i][]][a[i][]];
  114. }
  115. for(int i = ; i <= n1+n2; i++)
  116. {
  117. for(int j = ; j <= flag; j++)
  118. {
  119. int f = pre[i];
  120. int ff = relation[i];
  121. if(f == a[j][] && ff == a[j][])
  122. printf("%d\n", i);
  123. }
  124. }
  125. printf("end\n");
  126. }
  127. else
  128. printf("no\n");
  129. }
  130. return ;
  131. }

poj1417 true liars(并查集 + DP)详解的更多相关文章

  1. POJ1417 True Liars —— 并查集 + DP

    题目链接:http://poj.org/problem?id=1417 True Liars Time Limit: 1000MS   Memory Limit: 10000K Total Submi ...

  2. POJ1417 True Liars 并查集 动态规划 (种类并查集)

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1417 题意概括 有一群人,p1个好人,p2个坏人. 他们说了n句话.(p1+p2<=600,n ...

  3. poj1417 True Liars[并查集+背包]

    有一点小转化的题,在设计dp状态时还是有点费脑筋的. 地址. 依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛).一个"好人"域,一个"坏人"域,每句话分两 ...

  4. poj1308 Is It A Tree?(并查集)详解

    poj1308   http://poj.org/problem?id=1308 题目大意:输入若干组测试数据,输入 (-1 -1) 时输入结束.每组测试数据以输入(0 0)为结束标志.然后根据所给的 ...

  5. poj1182 食物链(种类并查集)详解

    poj 1182   http://poj.org/problem?id=1182 分析:这个题大意说的非常清楚了,就是求出假话的个数,题目中给的假话要求有三个 ① 当前的话与前面的某些真的话冲突,是 ...

  6. poj1417(种类并查集+dp)

    题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示 ...

  7. 数位DP 详解

    序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...

  8. POJ1417:True Liars(DP+带权并查集)

    True Liars Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

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

    Description After having drifted about in a small boat for a couple of days, Akira Crusoe Maeda was ...

随机推荐

  1. Ubuntu 12.04 和 Win7 双系统安装

    Thinkpad T400上成功安装双系统 安装Win7 使用光盘按步骤安装,到这里是一个没有分区的硬盘,做了如下分区: 100M(系统保留),40G(C盘),60G(D盘),80G(E盘),52G( ...

  2. 安全delete,添加refenerce,release

    #ifndef SAFE_ADDREF#define SAFE_ADDREF(p)    if (p != NULL) { p->AddRef(); }#endif #ifndef SAFE_R ...

  3. 安卓使用WIFI调试程序

    不知怎么回事,USB老是掉,真心烦.于是想着用WIFI可不可以调试呢,去百度一搜,果然可以.下面我说一下我的方案. 1.使用APWIFI创建WIFI热点,用安卓手机连接该热点,试试手机可以上网不. 2 ...

  4. 纯CSS的颜色渐变效果

    本例展示了一个纯css渐变的效果.其兼容IE6以上浏览器等各主流浏览器: 案例效果:查看演示 代码如下: css: *{margin:0;padding:0;} .linear{ width:100% ...

  5. 【CSS】Beginner3:Color

    1.red rgb(255,0,0) rgb(100%,0%,0%) #ff0000 #f00 2.Predefined color name aqua, black, blue, fuchsia, ...

  6. platform机制

    最近在看SPI.I2C这样简单点的总线驱动程 序,从Linux2.6起,内核引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver.现在Linux中 大部分的 ...

  7. HW3.2

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  8. 页面置换算法(最佳置换算法、FIFO置换算法、LRU置换算法、LFU置换算法)

    页面置换产生的原因是:分页请求式存储管理(它是实现虚拟存储管理的方法之一,其中一个特性是多次性-->多次将页面换入或换出内存) 效果最好的页面置换算法:最佳置换算法 比较常用的页面置换算法有:F ...

  9. POJ3280 - Cheapest Palindrome(区间DP)

    题目大意 给定一个字符串,要求你通过插入和删除操作把它变为回文串,对于每个字符的插入和删除都有一个花费,问你把字符串变为回文串最少需要多少花费 题解 看懂题立马YY了个方程,敲完就交了,然后就A了,爽 ...

  10. 使用 AppFuse 的七个理由

    mvn -e  archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes -DarchetypeArtifactId=appfus ...