poj1417 并查集+背包

2024-06-14 19:32
文章标签 背包 查集 poj1417

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

题目意思很明确,给出相对关系和各种类人数,让求是否能确定所有人所属的种类,说no的两个人必然不在一类,说yes的必然在一类。

种类划分,由于只给了相对关系,很容易想到用种类并查集,区间合并多取几次异或就行了。

划分完成后,形成多个集合,每个集合由两部分组成,分别为与根节点相同的一类和与根节点不同的一类,用一个sum数组记录每个集合中每类人数。

要确定是否能将所有集合划分成两部分,就需要明确两点:

1):每个集合形成的两个小集合每次取且仅能取且必须取一个小集合,将其划分为天使或魔鬼的一部分。

2):如果按上述方法对每个集合取一部分之后形成的划分成的两部分中一部分等于天使人数的策略仅存在一种,那么便能将所有人分为两部分。

特别需注意,每对相对关系必须分为两部分,且总和等于天使人数的策略仅存在一种才能划分。

代码:

#include <iostream>  
#include <cstring>  
#include <string>  
#include <algorithm>  using namespace std;  const int N=1000;  
int father[N];  
int sum[2][N];          //0代表一部分,1代表另一部分  
int rank[N];            //记录与根节点关系  
int dp[N][N];           //用来计算能否出现符合题意的策略  
int mark[N][N];         //用来记录该策略的路径  void init()  
{  memset(rank,0,sizeof(rank));  for (int i=0;i<N;i++)  {  father[i]=i;  sum[0][i]=1;  sum[1][i]=0;  }  
}  int find(int x)  
{  if (x==father[x])  return x;  int t=father[x];  father[x]=find(father[x]);  rank[x]=rank[x]^rank[t];  return father[x];  
}  void Union(int a,int b,int k)  
{  int x=find(a);  int y=find(b);  if (x==y)  return ;  father[y]=x;  rank[y]=k^rank[a]^rank[b];          //种类并查集和带权并查集常用手段,x->y=x->a->b->y  sum[0][x]+=sum[0^rank[y]][y];  sum[1][x]+=sum[1^rank[y]][y];   
}  int min(int a,int b)  
{  return a>b?b:a;  
}  int main()  
{  int n,p1,p2;  while (cin>>n>>p1>>p2&&n+p1+p2)  {  int a,b;  string s;  init();  for (int i=1;i<=n;i++)  {  cin>>a>>b>>s;  int k;  if (s[0]=='y')  k=0;  else  k=1;  Union(a,b,k);  }  int w1[1000];           //记录人数  int w2[1000];           //每个集合可划分为两部分,所以用相对的两个数组  int p[1000];            //记录每个集合的根节点  memset(w1,0,sizeof(w1));  memset(w2,0,sizeof(w2));  int cnt=1;  for (int i=1;i<=p1+p2;i++)  {        if (i==find(i))  {  w1[cnt]=sum[0][i];  w2[cnt]=sum[1][i];  p[cnt]=i;  cnt++;  }  }  memset(dp,0,sizeof(dp));  dp[0][0]=1;  /* for (int i=1;i<cnt;i++) cout<<i<<" "<<w1[i]<<" "<<w2[i]<<endl; */  memset(mark,0,sizeof(mark));  for (int i=1;i<cnt;i++)  {  for (int t=p1;t>=w1[i];t--)  {  if (dp[i-1][t-w1[i]])           //这点注意,由于每个集合必须要取,所以当前状态只能由前一状态推出,再之前的状态无用  {  dp[i][t]+=dp[i-1][t-w1[i]];         //记录这种状态的策略数,当前状态策略数由之前状态的策略数确定  mark[i][t]=0;  }  }  for (int t=p1;t>=w2[i];t--)  {if (dp[i-1][t-w2[i]])           //可以取w1,也可以取w2,但是两者仅能取一部分  {  dp[i][t]+=dp[i-1][t-w2[i]];         //同上  mark[i][t]=1;  }  }  }  //cout<<dp[cnt-1][p1]<<endl;  if (dp[cnt-1][p1]!=1)           //如果不能取到或者取到的策略不止一种,不能划分  cout<<"no"<<endl;  else  {  int ans[N];  int c=0;  cnt--;  int ss=p1+p2;  while (cnt>0)  {  //cout<<"AA "<<p1<<endl;  int cur=mark[cnt][p1];  int pp=p[cnt];  for (int i=1;i<=ss;i++)         //记录answer  {  if (find(i)==pp&&rank[i]==cur)  ans[c++]=i;  }      if (cur==0)  p1=p1-w1[cnt];  else  p1=p1-w2[cnt];  cnt--;      }  sort(ans,ans+c);  for (int i=0;i<c;i++)  cout<<ans[i]<<endl;  cout<<"end"<<endl;  }  }  
}


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



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

相关文章

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^