题意:有两种人,一种人只会说真话,另一种人只会说假话。只会说真话的人有p1个,另一种人有p2个。给出m个指令,每个指令为a b yes/no,意思是,如果为yes,a说b是只说真话的人,如果为no,a说b是只说假话的人。注意,a可以为b。保证每个指令都是正确的,且相互之间不矛盾。问,能不能确定哪些人是说真话的人,如果能,输出所有只说真话的人;如果不能,输出no。

解法:首先,用并查集建树,每个节点有两个参数,f和r,f表示父亲节点的编号,r表示与父亲节点是不是同一种人。r为0表示该节点与父亲节点是一种人,r为1表示该节点与父亲节点不是一种人。则用并查集处理之后,就可以分为k棵树,每棵树上可能有x[i]个人是同一种人,y[i]个是另一种人,(不确定哪个是说真话人的数量,哪个是说假话的人的数量),且每棵树之间互不影响。所以即是一个背包问题,用dp解决即可。

tag:并查集,DP,背包

 /*
* Author: Plumrain
* Created Time: 2013-11-28 10:26
* File Name: DS-POJ-1417.cpp
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map> using namespace std; #define CLR(x) memset(x, 0, sizeof(x))
#define CLR1(x) memset(x, -1, sizeof(x))
#define PB push_back
typedef pair<int, int> pii; struct oo{
int a, b, pos;
void clr(){
a = ; b = ;
}
}; struct node{
int f, r;
}; node p[];
pii num[];
vector<int> ans;
oo cnt[];
map<int, int> mp;
int n, m, p1, p2, d[][]; int find(int x)
{
if (x != p[x].f){
int y = p[x].f;
p[x].f = find(p[x].f);
p[x].r = (p[x].r + p[y].r) % ;
}
return p[x].f;
} void merge(int a, int b, int x, int t1, int t2)
{
p[t1].f = t2;
p[t1].r = (p[a].r + p[b].r + x) % ;
} void init()
{
for (int i = ; i <= n; ++ i)
cnt[i].clr(); for (int i = ; i <= n; ++ i){
p[i].f = i;
p[i].r = ;
} int a, b, x;
char s[];
for (int i = ; i < m; ++ i){
scanf ("%d%d%s", &a, &b, s);
if (s[] == 'y') x = ;
else x = ; int t1 = find(a), t2 = find(b);
if (t1 != t2)
merge(a, b, x, t1, t2);
}
} void gao0()
{
if (p2 == || p1 == ){
for (int i = ; i <= p1; ++ i)
printf ("%d\n", i);
printf ("end\n");
return;
}
printf ("no\n");
} int main()
{
while (scanf ("%d%d%d", &m, &p1, &p2) != EOF){
if (!m && !p1 && !p2) break; if (!m){
gao0();
continue;
} n = p1 + p2;
init(); if (p1 == p2){
printf ("no\n");
continue;
} CLR (num);
for (int i = ; i <= n; ++ i){
int y = find(i);
if (!p[i].r) ++ num[y].first;
else ++ num[y].second;
} int all = ;
for (int i = ; i <= n; ++ i)
if (num[i].first + num[i].second){
cnt[all].a = num[i].first;
cnt[all].b = num[i].second;
cnt[all].pos = i;
++ all;
} CLR1 (d);
d[][] = ;
for (int i = ; i < all; ++ i)
for (int j = ; j <= p1; ++ j){
if (j >= cnt[i].a && d[i-][j-cnt[i].a] >= ){
if (d[i][j] == -) d[i][j] = ;
d[i][j] += d[i-][j-cnt[i].a];
}
if (j >= cnt[i].b && d[i-][j-cnt[i].b] >= ){
if (d[i][j] == -) d[i][j] = ;
d[i][j] += d[i-][j-cnt[i].b];
}
} if (d[all-][p1] != ) printf ("no\n");
else{
int j = p1;
mp.clear();
for (int i = all-; i; -- i){
if (j >= cnt[i].a && d[i-][j-cnt[i].a] == ){
j -= cnt[i].a;
mp[cnt[i].pos] = ;
}
else if (j >= cnt[i].b && d[i-][j-cnt[i].b] == ){
j -= cnt[i].b;
mp[cnt[i].pos] = ;
}
} ans.clear();
for (int i = ; i <= n; ++ i){
int y = find(i);
if (mp.count(y) && mp[y] == p[i].r)
ans.PB (i);
} sort(ans.begin(), ans.end());
int sz = ans.size();
for (int i = ; i < sz; ++ i)
printf ("%d\n", ans[i]);
printf ("end\n");
} }
return ;
}

POJ 1417 True Liars的更多相关文章

  1. POJ 1417 - True Liars - [带权并查集+DP]

    题目链接:http://poj.org/problem?id=1417 Time Limit: 1000MS Memory Limit: 10000K Description After having ...

  2. poj 1417 True Liars(并查集+背包dp)

    题目链接:http://poj.org/problem?id=1417 题意:就是给出n个问题有p1个好人,p2个坏人,问x,y是否是同类人,坏人只会说谎话,好人只会说实话. 最后问能否得出全部的好人 ...

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

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

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

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

  5. True Liars POJ - 1417

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

  6. poj 1417(并查集+简单dp)

    True Liars Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 2087   Accepted: 640 Descrip ...

  7. POJ1417 True Liars

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

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

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

  9. POJ 1417 并查集 dp

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

随机推荐

  1. 使用EasyUI设计.net项目的菜单数实例

    最近领导说我们之前的项目采用的菜单树模型过时了,现在采用EasyUI来设计了,于是学习了第三方资源库easyUI,发觉果然是好东西,这里给大家分享下. 首先到官网下载源文件,这个是开源的,都可以下再, ...

  2. ASP.NET 3.5路由总结篇

    URL Routing是非常重要的一块技术体系,笔者将URL Routing的知识进行梳理后得出本文,旨在同大家分享,希望能够起到抛砖引玉的作用. 1.    什么是URL Routing? 所谓UR ...

  3. oracle还原数据库及遇到的问题

    1. 第一:用安装数据库时的管理员用户登录:创建一个新的用户,如: //创建用户123密码456 create user 123 identified by 456;第二:授权,赋予dba的权限 gr ...

  4. hdoj 2049 错排

    代码: #include <stdio.h> int main(){ int n,a,b,i,j; __int64 s[22],h[22]; s[1]=0; s[2]=1; s[3]=2; ...

  5. 浅谈 trie树 及其实现

    定义:又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构, 如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 核心思想:是空间换时间.利用字符串的公共前缀来降低查询时间的开 ...

  6. SGU 176.Flow construction (有上下界的最大流)

    时间限制:0.5s 空间限制:4M 题意: 有一个由管道组成的网络,有n个节点(n不大于100),1号节点可以制造原料,最后汇集到n号节点.原料通过管道运输.其中有一些节点有管道连接,这些管道都有着最 ...

  7. SWFUpload(转载)

    网上的例子介绍的文档真的很多.下面简单介绍一下 SWFUpload的文件上传流程是这样的: 1.引入相应的js文件 2.实例化SWFUpload对象,传入一个配置参数对象进行各方面的配置. 3.点击S ...

  8. linxu 挂载分区

    1. 添加新硬盘 设置 -> Storage -> SATA控制器->右击,选择“添加虚拟硬盘” 然后,根据需求创建合适的硬盘 2. 重启虚拟机 查看现有系统的磁盘空间 sudo f ...

  9. c# winform 设置winform进入窗口后在文本框里的默认焦点

    c# winform 设置winform进入窗口后在文本框里的默认焦点 进入窗口后默认聚焦到某个文本框,两种方法: ①设置tabindex 把该文本框属性里的tabIndex设为0,焦点就默认在这个文 ...

  10. 固定DIV样式

      <!doctype html>   <html>   <head>   <meta charset="UTF-8">   < ...