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;
我们分析输入的数据不难发现,对于输入x, y, yes,假设x为天使,则y也为为天使,若x为恶魔,那么y也为恶魔,即x, y, 同为恶魔或者天使;
对于输入x, y, no,同理可得x, y, 一者为天使一者为恶魔;即可得ch为yes时,x, y, 属同种,ch为no时, x, y属异种;
那么我们很容易就能想到种类并查集,rank[x]表示x与其父亲节点的关系,rank[x]=0表示x与其父亲节点属于同类,rank[1]表示x与其父亲节点属于异类;通过并查集将能确定相对关系的编号放在一个集合里面,每个结合里面的编号可以分为两部分,和根节点属同种的的节点,以及和根节点属于异种的节点;这样并不能直接确定答案,我们确定了划分集合的个数以及每个集合里面和根节点同种的节点数目以及异节点的数目;从每个集合里面选择一种节点,若所有选中的节点数目和为p的选择方法唯一,那么我们能够确定所有天使的编号,反之则不能;关于这个问题我们可以用dp完美解决;
事实上这个题目前面的并查集部分只是一个普通的种类并查集,这个记录路径的dp才是本题解的精妙部分;我们先用一个tot变量来存储集合个数(对于x==y的情况,我们让x单独为一个集合),并且用gg数组来标记所有编号属于的集合;用tag数组存储每个集合两种种类的数目;dp[i][j]表示到第i个集合选择种类的和为j的方法总数,即dp[tot][p]==1时能确定答案;对于dp过程中的每个选择我们用jj数组记录,然后反推选择路径用cc数组记录路径就ok啦~
代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#define MAXN 600
using namespace std; int pre[MAXN], rank[MAXN], gg[MAXN], jj[MAXN][MAXN], tag[MAXN][], dp[MAXN][MAXN], cc[MAXN][]; int find(int x){
if(x!=pre[x]){
int px=find(pre[x]);
rank[x]^=rank[pre[x]];
pre[x]=px;
}
return pre[x];
} void jion(int x, int y, int d){
int px=find(x);
int py=find(y);
if(px!=py){
pre[py]=px;
rank[py]=rank[x]^rank[y]^d;
}
} int main(void){
int m, p, q;
while(scanf("%d%d%d", &m, &p, &q)){
if(m+p+q==){
break;
}
for(int i=; i<=p+q; i++){ //**初始话
rank[i]=;
pre[i]=i;
}
while(m--){
int x, y, d=;
char ch[];
scanf("%d%d%s", &x, &y, ch);
if(ch[]=='y'){
d=;
}
jion(x, y, d);
}
memset(gg, , sizeof(gg)); //**gg存储集合个数并且给他们编号
memset(jj, , sizeof(jj));
memset(tag, , sizeof(tag));
memset(dp, , sizeof(dp));
memset(cc, , sizeof(cc));
int tot=;
for(int i=; i<=p+q; i++){ //**统计集合个数并且编号
if(find(i)==i){
gg[i]=++tot;
}
}
for(int i=; i<=p+q; i++){ //**分别统计每个集合两种类的数目并存储到tag中
tag[gg[find(i)]][rank[i]]++;
}
dp[][]=;
for(int i=; i<=tot; i++){
for(int j=; j<=p+q; j++){ //**dp[i][j]存储到第i个集合选择种类和为j的方法数
if(j-tag[i][]>=&&dp[i-][j-tag[i][]]){
dp[i][j]+=dp[i-][j-tag[i][]];
jj[i][j]=tag[i][]; //**jj数组记录路径,即选的是1还是0
}
if(j-tag[i][]>=&&dp[i-][j-tag[i][]]){
dp[i][j]+=dp[i-][j-tag[i][]];
jj[i][j]=tag[i][];
}
}
}
if(dp[tot][p]!=){
printf("no\n");
}else{
for(int i=tot,j=p; j>&&i>; i--){ //**标记路径
if(jj[i][j]==tag[i][]){
cc[i][]=;
}else{
cc[i][]=;
}
j-=jj[i][j];
}
for(int i=; i<=p+q; i++){
if(cc[gg[find(i)]][rank[i]]){
printf("%d\n", i);
}
}
printf("end\n");
}
}
return ;
}
poj1417(种类并查集+dp)的更多相关文章
- POJ 1417 True Liars(种类并查集+dp背包问题)
题目大意: 一共有p1+p2个人,分成两组,一组p1,一组p2.给出N个条件,格式如下: x y yes表示x和y分到同一组,即同是好人或者同是坏人. x y no表示x和y分到不同组,一个为好人,一 ...
- POJ1417 True Liars 并查集 动态规划 (种类并查集)
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ1417 题意概括 有一群人,p1个好人,p2个坏人. 他们说了n句话.(p1+p2<=600,n ...
- poj1417 true liars(并查集 + DP)详解
这个题做了两天了.首先用并查集分类是明白的, 不过判断是否情况唯一刚开始用的是搜索.总是超时. 后来看别人的结题报告, 才恍然大悟判断唯一得用DP. 题目大意: 一共有p1+p2个人,分成两组,一组p ...
- C - BLG POJ - 1417 种类并查集加dp(背包)
思路:刚看这道题感觉什么都不清楚,人物之间的关系一点也看不出来,都不知道怎么写,连并查集都没看出来,但是你可以仔细分析一下,当输入字符串为“yes”的时候,我们设输入的值为x和y,当x为天使是则由题可 ...
- NOI2001|POJ1182食物链[种类并查集 向量]
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 65430 Accepted: 19283 Description ...
- NOIP2010关押罪犯[并查集|二分答案+二分图染色 | 种类并查集]
题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨气值”(一个正整数值)来表示 ...
- POJ1703Find them, Catch them[种类并查集]
Find them, Catch them Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 42416 Accepted: ...
- poj1733(种类并查集+离散化)
题目链接: http://poj.org/problem?id=1733 题意: 输入n表示有一个长度为n的0,1字符串, m表示接下来有m行输入, 接下来的m行输入中x, y, even表示第x到第 ...
- poj 1182:食物链(种类并查集,食物链问题)
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 44168 Accepted: 12878 Description ...
随机推荐
- 关于CSS中对IE条件注释的问题
一.通用区分方式:IE6.IE7能识别*,标准浏览器(如FF)不能识别*:IE6能识别*,但不能识别 !important:IE7能识别*,也能识别 !important:IE8能识别\0,不能识别* ...
- PHP学习路线
0x1 0x2
- 2016年10月30日--JavaScript语法
1.基本数据类型:字符串.小数.整数.日期时间.布尔型等. 2.变量:[var]定义变量:var a:所有变量定义都用var定义,var是通用的可变类型. 3.类型转换:转为整数:parseInt() ...
- hibernate query.list() 返回的数据类型
在hibernate中,用hql语句查询实体类,采用list方法的返回结果为一个List,该List中封装的对象分为以下三种情况: 1.查询全部字段的情况下,如"from 实体类" ...
- python gui之tkinter事件处理
事件一览表 事件 代码 备注 鼠标左键单击按下 1/Button-1/ButtonPress-1 鼠标左键单击松开 ButtonRelease-1 鼠标右键单击 3 鼠标左键双击 Doub ...
- Genymotion自动化启动
一.启动方式 命令行: player.exe --vm-name [模拟器名称] 例子: "D:\Program files\Genymobile\Genymotion\player ...
- mkdir:批量创建文件夹
问题:mkdir dir[0-9]创建文件夹时,并没有如预期创建dir0~dir9这几个文件夹,而是创建了dir[0-9]这一个文件夹. 网上看了些相关资料,发现以前对[0-9]的理解不够透彻: &q ...
- Linux Vsftpd 连接超时解决方法
Linux Vsftpd 连接超时解决方法 2013-11-13 10:58:34| 分类: 默认分类|举报|字号 订阅 解决方法(http://www.lingdus.com/thread ...
- 【架构】MQTT/XMPP/GCM 等参考资料
https://www.zhihu.com/question/29138530 https://segmentfault.com/q/1010000002598843/a-10200000026014 ...
- Java总结(二):继承——Inheritance
关于继承: 1.为了重用代码——引入继承. 2.父类的某些方法反正要被重写,在父类里实现在也无用——引入抽象类. 3.把抽象类里的抽象方法抽出来——引入接口.