您的位置:首页 > 产品设计 > UI/UE

POJ 1417 True Liars(种类并查集+dp背包问题)

2013-09-03 15:12 531 查看
题目大意:

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

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

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

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

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

思路:

参考链接:/article/4679320.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=610;
int father[maxn];
int rel[maxn]; //rel[i]表示i与根节点的关系,0表示i与根节点在相同集合,1表示i与根节点在不同集合
vector<int> 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<maxn;i++){
b[i][0].clear();
b[i][1].clear();
a[i][0]=0;
a[i][1]=0;
}
memset(vis,0,sizeof(vis));
cnt=0;
//获取有哪些集合
for(int i=1;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]=1;
b[cnt][rel[j]].push_back(j);
a[cnt][rel[j]]++;
}
}
cnt++;
}
}
memset(dp,-1,sizeof(dp));
if(dfs(cnt-1,p1)!=1){
printf("no\n");
}
else{
vector<int> 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<b[i][0].size();j++){
ans.push_back(b[i][0][j]);
}
}
//如果选的是与根节点不在同一集合的元素
else if(t==a[i][1]){
for(int j=0;j<b[i][1].size();j++){
ans.push_back(b[i][1][j]);
}
}
tmp=tmp-t;
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++){
printf("%d\n",ans[i]);
}
printf("end\n");
}

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: