poj1417 True Liars[并查集+背包]

2023-10-31 18:40
文章标签 背包 查集 true liars poj1417

本文主要是介绍poj1417 True Liars[并查集+背包],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

地址。


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

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

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

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 9 #define ddbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
10 using namespace std;
11 typedef long long ll;
12 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
13 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
14 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
15 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
16 template<typename T>inline T read(T&x){
17     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
18     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
19 }
20 const int N=600+7;
21 int fa[N<<1],f[N][N>>1],cnt[N][N>>1],pos[N],tot,tot2,ans[N];
22 vector<int> a[N],b[N];
23 int n,m,gd,bd,x,y;
24 inline int Get(int x){return fa[x]^x?fa[x]=Get(fa[x]):x;}
25 
26 int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
27     while(read(m),read(gd),read(bd),m||gd||bd){
28         n=gd+bd;char s[5];tot=0,tot2=0;memset(pos,0,sizeof pos);
29         for(register int i=1;i<=n;++i)a[i].clear(),b[i].clear();
30         for(register int i=1;i<=(n<<1);++i)fa[i]=i;
31         for(register int i=1;i<=m;++i){
32             read(x),read(y),scanf("%s",s);
33             if(x==y)continue;
34             if(s[0]=='y')fa[Get(x)]=Get(y),fa[Get(x+n)]=Get(y+n);
35             else fa[Get(x)]=Get(y+n),fa[Get(x+n)]=Get(y);
36         }
37         for(register int i=1;i<=n;++i)if(Get(i)<=n)a[Get(i)].push_back(i);
38         for(register int i=n+1;i<=(n<<1);++i)if(Get(i)<=n)b[Get(i)].push_back(i-n);
39         for(register int i=1;i<=n;++i)if(!a[i].empty()||!b[i].empty())pos[++tot]=i;//联通块统计 
40         memset(f,0,sizeof f);memset(cnt,0,sizeof cnt);cnt[0][0]=1;
41         for(register int i=1;i<=tot;++i){
42             x=a[pos[i]].size(),y=b[pos[i]].size();
43             for(register int j=0,lx=j-x,ly=j-y;j<=gd;++j,lx=j-x,ly=j-y){
44                 if(lx<0&&ly>=0)f[i][j]=ly,cnt[i][j]=cnt[i-1][ly];
45                 else if(lx>=0&&ly<0)f[i][j]=lx,cnt[i][j]=cnt[i-1][lx];
46                 else if(lx>=0&&ly>=0)f[i][j]=cnt[i-1][lx]?lx:ly,cnt[i][j]=cnt[i-1][lx]+cnt[i-1][ly];
47             }
48         }//做dp 
49         if(cnt[tot][gd]^1)printf("no\n");
50         else{
51             int j=gd;
52             while(tot){
53                 if(j-f[tot][j]==a[pos[tot]].size()){for(register int i=0;i<a[pos[tot]].size();++i)ans[++tot2]=a[pos[tot]][i];}
54                 else for(register int i=0;i<b[pos[tot]].size();++i)ans[++tot2]=b[pos[tot]][i];
55                 j=f[tot--][j];
56             }//推回去 
57             sort(ans+1,ans+tot2+1);
58             for(register int i=1;i<=tot2;++i)printf("%d\n",ans[i]);
59             printf("end\n");
60         }
61     }
62     return 0;
63 }

 

转载于:https://www.cnblogs.com/saigyouji-yuyuko/p/10639594.html

这篇关于poj1417 True Liars[并查集+背包]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/317334

相关文章

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

csu(背包的变形题)

题目链接 这是一道背包的变形题目。好题呀 题意:给n个怪物,m个人,每个人的魔法消耗和魔法伤害不同,求打死所有怪物所需的魔法 #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>//#include<u>#include<map

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

uva 10130 简单背包

题意: 背包和 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>

poj 1182 并查集 食物链类

题意: 有n只动物,分别编号1....n。所有动物都属于A,B,C中的一种,已知A吃B,B吃C,C吃A。 按顺序给出下面两种共K条信息: 1. x 和 y 属于同一类。 2. x 吃 y 。 然而这些信息可能会出错,有可能有的信息和之前给出的信息矛盾,也有的信息可能给出的 x 和 y 不在n的范围内。 求k条信息中有多少条是不正确的。 解析: 对于每只动物,创建3个元素 i

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

多重背包转换成0-1背包

http://acm.hdu.edu.cn/showproblem.php?pid=2191 多重背包特点: 一种物品有C个(既不是固定的1个,也不是无数个) 优化的方法: 运用神奇的二进制,进行物品拆分,转化成01背包 物品拆分,把13个相同的物品分成4组(1,2,4,6) 用这4组可以组成任意一个1~13之间的数! 原理:一个数总可以用2^