欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - POJ1417


题意概括

  有一群人,p1个好人,p2个坏人。

  他们说了n句话。(p1+p2<=600,n<=1000)

  说话的格式是这样的:

  x y yes或者x y no

  分别表示x说y是/不是好人。

  其中好人说真话,坏人说假话。

  现在给出这些话。

  如果自相矛盾或者有多种满足条件的情况,那么输出no。

  否则从小到大输出好人的编号,输出完之后输出一个end(占一行)。

  有多组数据。

  注意n==0的情况


题解

  这题还是比较复杂的。

  首先跑一跑种类并查集。

  我们发现,一个人如果说了yes,那么x与y是一帮的,否则不是一帮的。

  于是,我们可以根据这个建立种类并查集,表示他们的敌对关系。

  然后我们首先可以把自相矛盾的情况判掉。

  (如果p1==p2,一定是no)

  然后我们把所有的关系处理出来,格式为:

  v,x,y,x,y分别为两个敌对集团的人数(y可以为0),而v表示x集团的祖先。

  假设总共弄出了cnt个敌对集团组。

  这个预处理方便了之后的操作。

  然后用dp[i][j]表示在前i个集团里面选择j个好人的方案数。

  我们可以用类似背包的做法求出整个dp数组。

  最后dp[cnt][p1]如果不等于1,那么要么多解,要么无解,所以输出no

  dp的过程中有可能会溢出,但是我们的dp只需要知道3种性质就可以了,分别是0,1,>1三种。

  想想为什么……

  所以当dp[i][j]比较大的时候,我们可以人为的把它弄小。

  不输出no的情况,我们从dp倒着回推,就可以找出方案。

  详细操作见代码。


代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
const int N=1005;
int n,m,p1,p2,fa[N*2],dp[N][N],cnt=0,f[N],ans[N],tot;
//dp[i][j]表示在前i个集合中选择j个好人的方案总数
struct Set{
int v,x,y;
Set (){}
Set (int a,int b,int c){
v=a,x=b,y=c;
}
}s[N];
int getf(int k){
return fa[k]==k?k:fa[k]=getf(fa[k]);
}
void plus(int &a,int b){
a=min(10,a+b);
}
void getans(int cnt,int rem){
if (cnt==0)
return;
int nc=cnt-1,rx=rem-s[cnt].x,ry=rem-s[cnt].y;
if (rx>=0&&dp[nc][rx]==1){
getans(nc,rx);
for (int i=1;i<=n;i++)
if (getf(i)==s[cnt].v)
ans[++tot]=i;
}
else if (ry>=0&&dp[nc][ry]==1){
getans(nc,ry);
for (int i=1;i<=n;i++)
if (getf(i+n)==s[cnt].v)
ans[++tot]=i;
}
}
int main(){
while (~scanf("%d%d%d",&m,&p1,&p2)&&(m||p1||p2)){
n=p1+p2;
for (int i=1;i<=n*2;i++)
fa[i]=i;
bool flag=1;
for (int i=1;i<=m;i++){
char s[5];
int a,b;
scanf("%d%d%s",&a,&b,s);
if (a==b&&s[0]=='n')
flag=0;
if (s[0]=='y'){
if (getf(a)==getf(b+n))
flag=0;
fa[getf(a)]=getf(b);
fa[getf(a+n)]=getf(b+n);
}
else {
if (getf(a)==getf(b))
flag=0;
fa[getf(a)]=getf(b+n);
fa[getf(b)]=getf(a+n);
}
}
if (!flag||p1==p2){
puts("no");
continue;
}
cnt=0;
memset(f,0,sizeof f);
for (int i=1;i<=n;i++){
if (f[i])
continue;
s[++cnt]=Set(getf(i),0,0);
for (int j=i;j<=n;j++){
if (f[j])
continue;
if (getf(j)==getf(i))
s[cnt].x++,f[j]=1;
else if (getf(j+n)==getf(i))
s[cnt].y++,f[j]=1;
}
}
memset(dp,0,sizeof dp);
dp[0][0]=1;
for (int i=1;i<=cnt;i++)
for (int j=0;j<=n;j++){
if (dp[i-1][j]==0)
continue;
int a=j+s[i].x,b=j+s[i].y;
if (a<=n)
plus(dp[i][a],dp[i-1][j]);
if (b<=n)
plus(dp[i][b],dp[i-1][j]);
}
if (dp[cnt][p1]!=1){
puts("no");
continue;
}
tot=0;
memset(ans,0,sizeof ans);
getans(cnt,p1);
sort(ans+1,ans+tot+1);
for (int i=1;i<=tot;i++)
printf("%d\n",ans[i]);
puts("end");
}
return 0;
}

  

POJ1417 True Liars 并查集 动态规划 (种类并查集)的更多相关文章

  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(DP+带权并查集)

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

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

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

  4. POJ1417 True Liars

    题意 Language:Default True Liars Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 6392 Accep ...

  5. BZOJ 4195: [Noi2015]程序自动分析 [并查集 离散化 | 种类并查集WA]

    题意: 给出若干相等和不等关系,判断是否可行 woc NOI考这么傻逼的题飞快打了一个种类并查集交上了然后爆零... 发现相等和不等看错了异或一下再叫woc90分 然后发现md$a \neq b, a ...

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

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

  7. POJ1417 True Liars 题解

    通过读题,容易发现,当回答为yes时 \(x,y\) 必属于同类,当回答为no时二者必为异类(并且当 \(x=y\) 时,回答必为yes,不过这题不用这个性质). 于是先按关系维护连通块,然后求出每个 ...

  8. 浅谈并查集&种类并查集&带权并查集

    并查集&种类并查集&带权并查集 前言: 因为是学习记录,所以知识讲解+例题推荐+练习题解都是放在一起的qvq 目录 并查集基础知识 并查集基础题目 种类并查集知识 种类并查集题目 并查 ...

  9. 洛谷 P1525 【关押罪犯】种类并查集

    题解 P1525 [关押罪犯]:种类并查集 前言: 在数据结构并查集中,种类并查集属于扩展域并查集一类. 比较典型的题目就是:食物链(比本题难一些,有三个种类存在) 首先讲一下本题的贪心,这个是必须要 ...

随机推荐

  1. CSS——margin

    CSS margin 属性 定义和用法 margin 简写属性在一个声明中设置所有外边距属性.该属性可以有 1 到 4 个值. 说明 这个简写属性设置一个元素所有外边距的宽度,或者设置各边上外边距的宽 ...

  2. webstorm11.0.3连接ftp

    一.工具 webstorm11.0.3 ftp工具:Beyond Compare 3 二.步骤 1.打开项目工程,按照下图中路径,打开ftp配置界面 2.在ftp配置界面 (1)右上角+号新建配置,选 ...

  3. mysql 原理 ~ innodb恢复机制

    举例说明 机制 数据页A的lsn为100,数据页B的lsn为200,checkpoint lsn为150,系统lsn为300,表示当前系统已经更新到300,小于150的数据页已经被刷到磁盘上,因此数据 ...

  4. 2018-2019-2 网络对抗技术 20165320 Exp4 恶意代码分析

    2018-2019-2 网络对抗技术 20165320 Exp4 恶意代码分析 一.实践目标 监控你自己系统的运行状态,看有没有可疑的程序在运行 分析一个恶意软件,就分析Exp2或Exp3中生成后门软 ...

  5. 【CTF REVERSE】ctf02-查找字符串

    1.前言 公司大拿给写的一个CTF逆向程序,提升我们组内人员的水平. 基于对话框MFC框架开发,使用EDIT控制特性隐藏Flag,可借助spy4win之类窗体工具找出Flag. 程序加UPX壳,已对壳 ...

  6. Simulink--MATLAB中的一种可视化仿真工具

     Simulink是MATLAB中的一种可视化仿真工具, 是一种基于MATLAB的框图设计环境,是实现动态系统建模.仿真和分析的一个软件包,被广泛应用于线性系统.非线性系统.数字控制及数字信号处理的建 ...

  7. dubbo系列四、dubbo服务暴露过程源码解析

    一.代码准备 1.示例代码 参考dubbo系列二.dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台) 2.简单了解下spring自定义标签 https://w ...

  8. llinux除了软连接本地文件夹同步:mount

    mount --bind /srv/dir1   /srv/dir2dir1:被共享的文件夹dir2:需要同步的文件夹

  9. 【Android开发】之Fragment与Acitvity通信

    上一篇我们讲到与Fragment有关的常用函数,既然Fragment被称为是“小Activity”,现在我们来讲一下Fragment如何与Acitivity通信.如果上一篇还有不懂得,可以再看一下.传 ...

  10. 如何把JS对象转成数组

    1. 前言 首先,当JS对象是键值对的情况时(Json对象),因为数组时以数字为索引的,所以只能把JS对象中的Key或者Value组成数组使用. 2. 样例如下: var obj={"one ...