poj1417 并查集+背包
来源:互联网 发布:张子凡 陆林轩 知乎 编辑:程序博客网 时间:2024/06/07 22:12
题目意思很明确,给出相对关系和各种类人数,让求是否能确定所有人所属的种类,说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; } } }
0 0
- poj1417 并查集+背包
- 【POJ1417】【True Liars】【加权并查集+背包+输出路径】
- POJ1417 True Liars (并查集+背包)
- POJ1417-DP+带权并查集
- poj1417 true liars(并查集 + DP)详解
- 并查集+背包
- 分组背包+并查集
- 分组背包dp+并查集 vijos1250
- 并查集与01背包
- Codevs_P3372 选学霸(并查集+DP+背包)
- poj1417
- POJ1417
- POJ--1417[True Liars] 并查集+背包
- CSU1326: The contest(并查集+分组背包)
- poj 1417 - True Liars(并查集+背包)
- 搭配购买(01背包+并查集)
- codevs 3372 选学霸(hash+并查集+多重背包)
- CSU-1326—分组背包+并查集
- 面唯一需要解释的是下面几行
- C#利用钩子控制鼠标
- 小米染指平板电脑凶多吉少
- Flex 数组 Array 用法---gson转换为flex array的方法--两个Array()/as Array
- C#中判断空字符串的3种方法性能分析
- poj1417 并查集+背包
- C#优化字符串操作
- 主外键关联删除(on delete set null和on delete cascade)
- windows运行命令大全
- 上一篇讲到最简单的表单处理,就将输入的字符串输出了。
- 数据库连接类的写法
- C#的6种常用集合类大比拼
- json转换java和java转json的方法
- C#开发编码规范