题目大意:

一共有p1+p2个人,分成两组,一组p1,一组p2。给出N个条件,格式如下:

x y yes表示x和y分到同一组,即同是好人或者同是坏人。

x y no表示x和y分到不同组,一个为好人,一个为坏人

这个可以自己分析一下, 很简单。

问分组情况是否唯一,若唯一则输出p1的成员,否则输出no。保证不存在矛盾条件,但是有可能出现x=y的情况。

思路:

参考链接:http://www.cnblogs.com/kuangbin/archive/2013/04/06/3002498.html

用rel[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。

(可以举类验证一下,假如1、2、3,前者分别是后者的父亲。yes=0,no=1,这样才能满足:3相对2的关系+2相对1的关系=3相对1的关系。如果yes=1,no=0,则关系不成立)

处理之后,还需要判断是否唯一。

我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,只知道节点相对根节点的关系,0为一组,1为另一组)

接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。

背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种。

如果dp[cnt][p1]!=1说明方案不唯一,或者无解。

输出方案就是加个pre数组,从后往前递推。

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector> using namespace std;
const int maxn=;
int father[maxn];
int rel[maxn]; //rel[i]表示i与根节点的关系,0表示i与根节点在相同集合,1表示i与根节点在不同集合
vector<int> b[maxn][]; //b[i][0]存储与i在相同集合的点,b[i][1]存储与i在不同集合的点
int a[maxn][]; //a[i][0]存储与i在相同集合的点的个数,a[i][1]存储与i在不同集合的点的个数
int num[maxn][]; //num[i][0]存储与i在相同集合的点的个数(包括i),num[i][1]存储与i在不同集合的个数(包括i)
int dp[maxn][]; //dp[i][j]表示前i组选取j个人为好人的方案数。
//用于最后输出好人的编号,pre[i][j]对应dp[i][j],存储的是从第i组选的好人个数(只有两种情况,要么是好人的个数,要么是坏人的个数)
int pre[maxn][];
int vis[maxn];
int n,p1,p2,cnt; void init(){
//i应该是<=p1+p2,一开始写为n了。。。
for(int i=;i<=p1+p2;i++){
father[i]=i;
rel[i]=; //一开始初始化为1了,囧。。。
}
}
int find_root(int x){
if(father[x]==x)
return x;
int fa=father[x];
father[x]=find_root(father[x]);
rel[x]=(rel[x]+rel[fa])%;
return father[x];
}
void Union(int a,int b){
father[b]=a;
} int dfs(int i,int num){
//该条件一开始都忘写了,结果导致超时。。。
if(dp[i][num]!=-){
return dp[i][num];
}
if(i==){
if(a[i][]==a[i][]){
if(num==a[i][])
dp[i][num]=;
else
dp[i][num]=;
}
else if( num==a[i][] || num==a[i][]){
dp[i][num]=;
pre[i][num]=num; //这里都忘记写了,额
}
else{
dp[i][num]=;
}
return dp[i][num];
}
dp[i][num]=;
if(num>=a[i][] && dfs(i-,num-a[i][])){
dp[i][num]+=dp[i-][num-a[i][]];
pre[i][num]=a[i][];
}
if(num>=a[i][] && dfs(i-,num-a[i][])){
dp[i][num]+=dp[i-][num-a[i][]];
pre[i][num]=a[i][];
} return dp[i][num]; }
int main()
{
int x,y,k,tmp1,tmp2;
char ch[];
while(scanf("%d%d%d",&n,&p1,&p2)){
if(n== && p1== && p2==)
break;
init();
for(int i=;i<=n;i++){
scanf("%d%d%s",&x,&y,ch);
int fx=find_root(x);
int fy=find_root(y);
if(ch[]=='y')
k=;
else
k=;
if(fx!=fy){
Union(fx,fy);
rel[fy]=(rel[y]+k+rel[x])%;
}
}
for(int i=;i<maxn;i++){
b[i][].clear();
b[i][].clear();
a[i][]=;
a[i][]=;
}
memset(vis,,sizeof(vis));
cnt=;
//获取有哪些集合
for(int i=;i<=p1+p2;i++){
if(!vis[i]){
tmp1=find_root(i);
for(int j=i;j<=p1+p2;j++){
tmp2=find_root(j);
if(tmp2==tmp1){
vis[j]=;
b[cnt][rel[j]].push_back(j);
a[cnt][rel[j]]++;
}
}
cnt++;
}
}
memset(dp,-,sizeof(dp));
if(dfs(cnt-,p1)!=){
printf("no\n");
}
else{
vector<int> ans;
int tmp=p1,t;
for(int i=cnt-;i>=;i--){
t=pre[i][tmp];
//如果选的是与根节点在同一集合的元素
if(t==a[i][]){
for(int j=;j<b[i][].size();j++){
ans.push_back(b[i][][j]);
}
}
//如果选的是与根节点不在同一集合的元素
else if(t==a[i][]){
for(int j=;j<b[i][].size();j++){
ans.push_back(b[i][][j]);
}
}
tmp=tmp-t;
}
sort(ans.begin(),ans.end());
for(int i=;i<ans.size();i++){
printf("%d\n",ans[i]);
}
printf("end\n");
} }
return ;
}

POJ 1417 True Liars(种类并查集+dp背包问题)的更多相关文章

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

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

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

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

  3. poj1417(种类并查集+dp)

    题目:http://poj.org/problem?id=1417 题意:输入三个数m, p, q 分别表示接下来的输入行数,天使数目,恶魔数目: 接下来m行输入形如x, y, ch,ch为yes表示 ...

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

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

  5. poj 1182 食物链(种类并查集 ‘初心者’)

    题目链接:http://poj.org/problem?id=1182 借着这题可以好好理解一下种类并查集,这题比较简单但挺经典的. 题意就不解释了,中问题. 关于种类并查集结局方法也是挺多的 1扩增 ...

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

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

  7. POJ 1182 食物链(种类并查集)

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

  8. POJ - 1733 Parity game 种类并查集+离散化

    思路:d(i, j)表示区间(i, j]的1的个数的奇偶性.输入最多共有5000*2个点,需要离散化处理一下.剩下的就是并查集判冲突. AC代码 #include <cstdio> #in ...

  9. POJ 1182 食物链 (种类并查集)

    动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种.有人用两种说 ...

随机推荐

  1. 使用原生Sql查询实现按分类推送最新文章到首页

    一般在网站的首页都会有网站最新文章的推送,而这些文章又属于不同的分类.如果某个分类的文章突然集中在一个时间段发布,那么就会造成首页上所有文章都是该分类的文章,其他的文章分类就变成不可见的了.所以,我希 ...

  2. 【风马一族_xml】xml的基本讲解笔记

    xml是如何保存数据的 在xml语言中,它允许用户自定义标签.每个标签用于描述一段数据; 一个标签可以分为开始标签和结束标签,在开始标签和结束标签之间又可以嵌套其它标签,利用标签间的嵌套其它标签,利用 ...

  3. DEDECMS中,文章页直接输出字段名

    文章页中,可直接输出字段名

  4. Java HttpURLConnection 抓取网页内容 解析gzip格式输入流数据并转换为String格式字符串

    最近GFW为了刷存在感,搞得大家是头晕眼花,修改hosts 几乎成了每日必备工作. 索性写了一个小程序,给办公室的同事们分享,其中有个内容 就是抓取网络上的hosts,废了一些周折. 我是在一个博客上 ...

  5. IE8浏览器跨域接口访问异常的解决办法

    IE8版本以下浏览器绝对是一个神奇的存在,忙碌好久,万事具备,居然在ajax调用接口的时候直接爆炸 陈述一下问题 首先是有这样一个接口,请求类型POST,入参JSON,出参JSON,jQuery aj ...

  6. 获取SilverLight.Web项目中路径Uri

    方法一: //获取指定要呈现的xaml内容的包活xaml文件Uri var strFullUrl = Application.Current.Host.Source.AbsoluteUri; if ( ...

  7. php中url传递中文字符,特殊危险字符的解决方法

    php中的urldecode,base64_encode函数然后再结合自己写的替换函数来进行安全传递url中文字符,特殊危险字符. 需要在url中传递中文字符或是其它的html等特殊字符,似乎总会有各 ...

  8. 2013-07-26 IT 要闻速记快想

    ### ========================= ###传Google正在内测供用户买卖技能的电商平台Helpout,最早于下月上线该服务将依托Google强大的云服务和搜索能力,以实时视频 ...

  9. MySQL excel导入错误 Out of range value adjusted for column

    修改my.ini,将 sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"改为 sql ...

  10. ActiveMQ之TemporaryQueue和TemporaryTopic

    TemporaryQueue和TemporaryTopic,从字面上就可以看出它们是“临时”的目的地.可以通过Session来创建,例如: TemporaryQueue replyQueue = se ...