题目: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)的更多相关文章

  1. POJ 1417 True Liars(种类并查集+dp背包问题)

    题目大意: 一共有p1+p2个人,分成两组,一组p1,一组p2.给出N个条件,格式如下: x y yes表示x和y分到同一组,即同是好人或者同是坏人. x y no表示x和y分到不同组,一个为好人,一 ...

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

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

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

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

  4. C - BLG POJ - 1417 种类并查集加dp(背包)

    思路:刚看这道题感觉什么都不清楚,人物之间的关系一点也看不出来,都不知道怎么写,连并查集都没看出来,但是你可以仔细分析一下,当输入字符串为“yes”的时候,我们设输入的值为x和y,当x为天使是则由题可 ...

  5. NOI2001|POJ1182食物链[种类并查集 向量]

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 65430   Accepted: 19283 Description ...

  6. NOIP2010关押罪犯[并查集|二分答案+二分图染色 | 种类并查集]

    题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用“怨气值”(一个正整数值)来表示 ...

  7. POJ1703Find them, Catch them[种类并查集]

    Find them, Catch them Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 42416   Accepted: ...

  8. poj1733(种类并查集+离散化)

    题目链接: http://poj.org/problem?id=1733 题意: 输入n表示有一个长度为n的0,1字符串, m表示接下来有m行输入, 接下来的m行输入中x, y, even表示第x到第 ...

  9. poj 1182:食物链(种类并查集,食物链问题)

    食物链 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 44168   Accepted: 12878 Description ...

随机推荐

  1. 给groupBox添加滚动条

    public Form3() { InitializeComponent(); foreach (Control gbox in groupBox1.Controls) { if (gbox is V ...

  2. Android学习笔记(四)——再探Intent

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 我们可以使用 Intent 来启动一个活动, 还可以在启动活动的时候传递数据的,下面一起来看一下: 一.向下一 ...

  3. angularjs入门基础一

    app.controller('firstController',function($scope,$rootScope){ $scope.name='张三'; $rootScope.age='30'; ...

  4. [BZOJ1833][ZJOI2010]count 数字计数

    [BZOJ1833][ZJOI2010]count 数字计数 试题描述 给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. 输入 输入文件中仅包含一行两个整数a ...

  5. hiho #1326 : 有序01字符串

    #1326 : 有序01字符串 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于一个01字符串,你每次可以将一个0修改成1,或者将一个1修改成0.那么,你最少需要修改 ...

  6. Qt5 程序启动画面动图效果

    2333终于实现动图,先弄了一个窗口去掉标题栏假装就是启动画面了,还是那只萌萌的猫这次会动了! 基类用的是QWidget  类名称MainView #ifndef MAINVIEW_H #define ...

  7. BZOJ 1090: [SCOI2003]字符串折叠

    Sol 区间DP. 转移很简单,枚举会形成的断长转移就行,话说上一题我就跟这个是差不多的思路,转移改了改,然后死活过不了... 同样都是SCOI的题...相差4年... Code /********* ...

  8. dp水题 序列问题 (9道)

    9道题.A了8道,A题看题解也没弄懂怎么维护m段子序列的,过一段时间再回来看看     dp试水 47:56:23 125:00:00   Overview Problem Status Rank ( ...

  9. POJ 3281 网络流dinic算法

    B - Dining Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit S ...

  10. 【POI】修改Excel内容

    package com.what21.test; import java.io.File; import java.io.FileInputStream; import java.io.FileNot ...