有一点小转化的题,在设计dp状态时还是有点费脑筋的。

地址


依题意,首先可以知道肯定要扩展域的并查集(明摆着的嘛)。一个"好人"域,一个"坏人"域,每句话分两种情况考虑连边。假设是yes,同域连边,否则异域连边(经典模型嘛)。然后就是要考虑如何验证是否有$x$个好人$y$个坏人的唯一解存在。这取决于联通块。

可以参考我瞎画的图,上面点1~N,下面点N+1~2N。

由于并查集合并时操作的对称性,可以发现一个联通块要么$x$个好人$y$个坏人要么$y$个好人$x$个坏人。那么对于所有联通块必须选其中一种方案,最后要凑齐。于是我就想到二维的背包。。但是复杂度太大了啊。。卡了好久,于是又手玩了样例。发现当我所有联通块恰好凑出x个好人时,剩下的不就全是坏人吗。所以只要去做一个好人的背包就行了。dp的时候由于没处理好关于多解的问题,又调了半小时。。一题做两个小时我也是醉了。。其实就是有前面一个状态转移,记一下$pre$。在记一下方案。最终好人的背包装满的状态方案不是1种就是无解,是一种就把所有好人找出来,这个我开了vector存了每个联通块。细节还看code,虽然可能写繁掉了qwq。。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef long long ll;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=+;
int fa[N<<],f[N][N>>],cnt[N][N>>],pos[N],tot,tot2,ans[N];
vector<int> a[N],b[N];
int n,m,gd,bd,x,y;
inline int Get(int x){return fa[x]^x?fa[x]=Get(fa[x]):x;} int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
while(read(m),read(gd),read(bd),m||gd||bd){
n=gd+bd;char s[];tot=,tot2=;memset(pos,,sizeof pos);
for(register int i=;i<=n;++i)a[i].clear(),b[i].clear();
for(register int i=;i<=(n<<);++i)fa[i]=i;
for(register int i=;i<=m;++i){
read(x),read(y),scanf("%s",s);
if(x==y)continue;
if(s[]=='y')fa[Get(x)]=Get(y),fa[Get(x+n)]=Get(y+n);
else fa[Get(x)]=Get(y+n),fa[Get(x+n)]=Get(y);
}
for(register int i=;i<=n;++i)if(Get(i)<=n)a[Get(i)].push_back(i);
for(register int i=n+;i<=(n<<);++i)if(Get(i)<=n)b[Get(i)].push_back(i-n);
for(register int i=;i<=n;++i)if(!a[i].empty()||!b[i].empty())pos[++tot]=i;//联通块统计
memset(f,,sizeof f);memset(cnt,,sizeof cnt);cnt[][]=;
for(register int i=;i<=tot;++i){
x=a[pos[i]].size(),y=b[pos[i]].size();
for(register int j=,lx=j-x,ly=j-y;j<=gd;++j,lx=j-x,ly=j-y){
if(lx<&&ly>=)f[i][j]=ly,cnt[i][j]=cnt[i-][ly];
else if(lx>=&&ly<)f[i][j]=lx,cnt[i][j]=cnt[i-][lx];
else if(lx>=&&ly>=)f[i][j]=cnt[i-][lx]?lx:ly,cnt[i][j]=cnt[i-][lx]+cnt[i-][ly];
}
}//做dp
if(cnt[tot][gd]^)printf("no\n");
else{
int j=gd;
while(tot){
if(j-f[tot][j]==a[pos[tot]].size()){for(register int i=;i<a[pos[tot]].size();++i)ans[++tot2]=a[pos[tot]][i];}
else for(register int i=;i<b[pos[tot]].size();++i)ans[++tot2]=b[pos[tot]][i];
j=f[tot--][j];
}//推回去
sort(ans+,ans+tot2+);
for(register int i=;i<=tot2;++i)printf("%d\n",ans[i]);
printf("end\n");
}
}
return ;
}

poj1417 True Liars[并查集+背包]的更多相关文章

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

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

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

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

  3. poj1417 带权并查集 + 背包 + 记录路径

    True Liars Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 2713   Accepted: 868 Descrip ...

  4. poj1417(带权并查集+背包DP+路径回溯)

    题目链接:http://poj.org/problem;jsessionid=8C1721AF1C7E94E125535692CDB6216C?id=1417 题意:有p1个天使,p2个恶魔,天使只说 ...

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

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

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

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

  7. poj1417 带权并查集+0/1背包

    题意:有一个岛上住着一些神和魔,并且已知神和魔的数量,现在已知神总是说真话,魔总是说假话,有 n 个询问,问某个神或魔(身份未知),问题是问某个是神还是魔,根据他们的回答,问是否能够确定哪些是神哪些是 ...

  8. POJ1417 True Liars

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

  9. POJ - 1417 并查集+背包

    思路:很简单的种类并查集,利用并查集可以将所有的人分成几个集合,每个集合又分为好人和坏人集合,直接进行背包dp判断有多少种方法可以在取了所有集合并且人数正好凑足p1个好人的方案.dp(i, j)表示前 ...

随机推荐

  1. 【Android】getActionBar()为null的解决方法总结

    前言 在使用 ActionBar的时候,有时候会爆出空指针异常,这是由于应用没有获取到 ActionBar 导致的,而导致应用没有获取到 ActionBar 的原因比較多.所以我们以下就来总结一下 A ...

  2. HTML元素定位

    一切皆为框 div.h1 或 p 元素常常被称为块级元素(block element).这意味着这些元素显示为一块内容,即"块框".与之相反,span 和 strong 等元素称为 ...

  3. C# 实现和调用自定义扩展方法

    定义和调用扩展方法 定义一个静态类以包含扩展方法.该类必须对客户端代码可见. 将该扩展方法实现为静态方法,并使其至少具有与包含类相同的可见性. 该方法的第一个参数指定方法所操作的类型:该参数必须以 t ...

  4. 简单的js表单验证框架

    /** * 通常在我们的HTML页面表单中有大量的数据验证工作, * 免不了要写很多验证表单的js代码,这是一项非常繁琐 * 枯燥的工作.很多程序员也会经常遗漏这项工作.当然 * 一些JavaEE框架 ...

  5. 身份证归属地查询免费api接口代码

    描写叙述 :依据身份证编号 查询归属地信息. 身份证实体类: package org.wx.xhelper.model; /** * 身份证实体类 * @author wangxw * @versio ...

  6. php解码“&#”编码的中文用函数html_entity_decode()

    遇到类似 ' 这种编码的字,我们可以用html_entity_decode()函数来解码. html_entity_decode() 函数把 HTML 实体转换为字符. 语法 html_entity_ ...

  7. redis数据迁移操作

    redis客户端连接命令,分别连接旧环境中的主从redis Src目录下./redis-cli -h IP -p PORT 使用info replication 命令找出主redis使用客户端命令连接 ...

  8. Mongodb亿级数据量的性能测试

    进行了一下Mongodb亿级数据量的性能测试,分别测试如下几个项目:   (所有插入都是单线程进行,所有读取都是多线程进行) 1) 普通插入性能 (插入的数据每条大约在1KB左右) 2) 批量插入性能 ...

  9. Navicat Premium创建事件计划调用MySql存储过程

    1.检查事件计划,操作:工具——命令行界面——执行命令 show variables like '%event_scheduler%'; (分号不能丢)—— event_scheduler  ON 表 ...

  10. MongoDB在win7上的安装(精简版)

    1.下载mongdb的zip文件,解压后会发现有bin文件夹,在同层目录下建一个data目录, 2.在data目录下建一个log和db文件夹, 3.在log文件下建一个MongoDB.log 文件 4 ...