博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POJ 1417 True Liars(种类并查集+dp背包问题)
阅读量:4563 次
发布时间:2019-06-08

本文共 2480 字,大约阅读时间需要 8 分钟。

题目大意:

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

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

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

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

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

 

思路:

参考链接:

 

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

 

 

转载于:https://www.cnblogs.com/chenxiwenruo/p/3298767.html

你可能感兴趣的文章
for循环
查看>>
【转】Android手机客户端关于二维码扫描的源码--不错
查看>>
【转】Java 多线程(四) 多线程访问成员变量与局部变量
查看>>
【转】gcc warning: braces around scalar initializer (标量初始化的括号)
查看>>
C/C++内存泄漏及检测(vs2005平台)【转】
查看>>
SpringBoot中遇到的问题---【Whitelabel Error Page 404 spring boot解决方法】
查看>>
python之路--模块--景丽洋
查看>>
postfix队列管理
查看>>
编译安装nginx
查看>>
操作系统的硬件环境
查看>>
js三种定义类的方法
查看>>
LeetCode——Unique Binary Search Trees
查看>>
Python运算符及基本数据类型
查看>>
noip2006提高组题解
查看>>
最短路(数据处理):HDU 5817 Ice Walls
查看>>
sass揭秘之@mixin,%,@function scss基本使用及操作函数
查看>>
自定义UITabbarController控制器
查看>>
刮奖效果
查看>>
[runtime] iOS-Runtime-Headers
查看>>
读文章有感
查看>>