poj1417 true liars(并查集 + DP)详解
这个题做了两天了。首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索。总是超时。 后来看别人的结题报告, 才恍然大悟判断唯一得用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的情况是否唯一。
- #include<iostream>
- #include<cstdio>
- #include<string.h>
- using namespace std;
- int n, n1, n2, mi, mx, key, flag, a[][], vis[][][], ans[][], v[], d[][], pre[], relation[];
- int find(int x)//寻找最根部的父亲节点
- {
- if(pre[x] == x)
- return x;
- else if(pre[x] != x)
- {
- int t = pre[x];
- pre[x] = find(pre[x]);//边寻找最根部父亲节点,边更新原父亲节点的信息
- relation[x] = (relation[t] + relation[x]) % ;//类似于种类并查集的更新
- }
- return pre[x];
- }
- void work(int a, int b, int c)//合并节点
- {
- int fx = find(a);
- int fy = find(b);
- if(fx != fy)
- {
- pre[fx] = fy;
- relation[fx] = (relation[a] + relation[b] + c) % ;
- }
- }
- void dp()
- {
- int k = ;
- for(int i = mi+; i <= (n1+n2); i++)
- {
- if(v[i] == )
- {
- k++;
- int t1 = ans[i][];
- int t2 = ans[i][];
- int mx = min(t1, t2);
- for(int j = n1; j >= mx; j--)
- {
- d[k][j] = d[k-][j-t1] + d[k-][j-t2];
- if(d[k-][j-t1] == && d[k-][j-t2] == )
- {
- vis[k][j][] = i;
- vis[k][j][] = ;
- }
- else if(d[k-][j-t2] == && d[k-][j-t1] == )
- {
- vis[k][j][] = i;
- vis[k][j][] = ;
- }
- }
- }
- }
- }
- int main()
- {
- while(scanf("%d%d%d", &n, &n1, &n2) != EOF)
- {
- if(n == && n1 == && n2 == )
- break;
- //初始化所有节点得父节点和relation
- for(int i = ; i <= n1+n2; i++){pre[i] = i; relation[i] = ;}
- for(int i = ; i <= n; i++)
- {
- int a, b;
- char c[];
- scanf("%d%d%s", &a, &b, &c);
- if(strcmp(c, "yes") == )
- work(a, b, );
- else if(strcmp(c, "no") == )
- work(a, b, );
- }
- //这里注意一下:到这一步,有可能存在一些点的父节点不是最跟的节点
- for(int i = ; i <= n1+n2; i++)
- int t = find(i);
- memset(v, , sizeof(v));
- //ans[i][0]表示与最根节点i同类的节点个数, ans[i][1]代表与最根节点i不同类的节点个数
- memset(ans, , sizeof(ans));
- flag = ;//存储一共有多少个不同的最跟部的父节点
- mi = 10e9;
- for(int i = ; i <= n1+n2; i++)
- {
- int x = pre[i];
- int y = relation[i];
- if(x < mi)
- mi = x;
- ans[x][y]++;
- if(v[x] == )
- {
- v[x] = ;
- flag++;
- }
- }
- /*d[i][j]前i个集合中累计人数为j时有多少种可能, vis[i][j][]保存每一个集合选了哪一类, 为最后输出用。 vis[i][j][0]表示前i个集合中累计人数为j时的最根节点,vis[i][j][1] 表示前i个集合中累计人数为j时,最根节点为vis[i][j][0]时,与根节点的关系*/
- memset(d, , sizeof(d));
- memset(vis, , sizeof(vis));
- d[][ans[mi][]]++; d[][ans[mi][]]++;
- vis[][ans[mi][]][] = mi;
- vis[][ans[mi][]][] = ;
- vis[][ans[mi][]][] = mi;
- vis[][ans[mi][]][] = ;
- dp();
- if(d[flag][n1] == )//如果前flag个集合中累计人数为n1的可能为1时,有唯一解
- {
- int j = n1;
- for(int i = flag; i >= ; i--)//从后往前推
- {
- a[i][] = vis[i][j][];
- a[i][] = vis[i][j][];//记录第i个集合中取得是哪一类(同类0, 不同类1)
- j -= ans[a[i][]][a[i][]];
- }
- for(int i = ; i <= n1+n2; i++)
- {
- for(int j = ; j <= flag; j++)
- {
- int f = pre[i];
- int ff = relation[i];
- if(f == a[j][] && ff == a[j][])
- printf("%d\n", i);
- }
- }
- printf("end\n");
- }
- else
- printf("no\n");
- }
- return ;
- }
poj1417 true liars(并查集 + DP)详解的更多相关文章
- POJ1417 True Liars —— 并查集 + DP
题目链接:http://poj.org/problem?id=1417 True Liars Time Limit: 1000MS Memory Limit: 10000K Total Submi ...
- POJ1417 True Liars 并查集 动态规划 (种类并查集)
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1417 题意概括 有一群人,p1个好人,p2个坏人. 他们说了n句话.(p1+p2<=600,n ...
- poj1417 True Liars[并查集+背包]
有一点小转化的题,在设计dp状态时还是有点费脑筋的. 地址. 依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛).一个"好人"域,一个"坏人"域,每句话分两 ...
- poj1308 Is It A Tree?(并查集)详解
poj1308 http://poj.org/problem?id=1308 题目大意:输入若干组测试数据,输入 (-1 -1) 时输入结束.每组测试数据以输入(0 0)为结束标志.然后根据所给的 ...
- poj1182 食物链(种类并查集)详解
poj 1182 http://poj.org/problem?id=1182 分析:这个题大意说的非常清楚了,就是求出假话的个数,题目中给的假话要求有三个 ① 当前的话与前面的某些真的话冲突,是 ...
- poj1417(种类并查集+dp)
题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示 ...
- 数位DP 详解
序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...
- POJ1417:True Liars(DP+带权并查集)
True Liars Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 【POJ1417】【带标记并查集+DP】True Liars
Description After having drifted about in a small boat for a couple of days, Akira Crusoe Maeda was ...
随机推荐
- Ubuntu 12.04 和 Win7 双系统安装
Thinkpad T400上成功安装双系统 安装Win7 使用光盘按步骤安装,到这里是一个没有分区的硬盘,做了如下分区: 100M(系统保留),40G(C盘),60G(D盘),80G(E盘),52G( ...
- 安全delete,添加refenerce,release
#ifndef SAFE_ADDREF#define SAFE_ADDREF(p) if (p != NULL) { p->AddRef(); }#endif #ifndef SAFE_R ...
- 安卓使用WIFI调试程序
不知怎么回事,USB老是掉,真心烦.于是想着用WIFI可不可以调试呢,去百度一搜,果然可以.下面我说一下我的方案. 1.使用APWIFI创建WIFI热点,用安卓手机连接该热点,试试手机可以上网不. 2 ...
- 纯CSS的颜色渐变效果
本例展示了一个纯css渐变的效果.其兼容IE6以上浏览器等各主流浏览器: 案例效果:查看演示 代码如下: css: *{margin:0;padding:0;} .linear{ width:100% ...
- 【CSS】Beginner3:Color
1.red rgb(255,0,0) rgb(100%,0%,0%) #ff0000 #f00 2.Predefined color name aqua, black, blue, fuchsia, ...
- platform机制
最近在看SPI.I2C这样简单点的总线驱动程 序,从Linux2.6起,内核引入了一套新的驱动管理和注册机制:Platform_device和Platform_driver.现在Linux中 大部分的 ...
- HW3.2
import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...
- 页面置换算法(最佳置换算法、FIFO置换算法、LRU置换算法、LFU置换算法)
页面置换产生的原因是:分页请求式存储管理(它是实现虚拟存储管理的方法之一,其中一个特性是多次性-->多次将页面换入或换出内存) 效果最好的页面置换算法:最佳置换算法 比较常用的页面置换算法有:F ...
- POJ3280 - Cheapest Palindrome(区间DP)
题目大意 给定一个字符串,要求你通过插入和删除操作把它变为回文串,对于每个字符的插入和删除都有一个花费,问你把字符串变为回文串最少需要多少花费 题解 看懂题立马YY了个方程,敲完就交了,然后就A了,爽 ...
- 使用 AppFuse 的七个理由
mvn -e archetype:generate -B -DarchetypeGroupId=org.appfuse.archetypes -DarchetypeArtifactId=appfus ...