POJ - 1417 True Liars POJ - 141 带权并查集,01背包问题

题目链接

POJ-1417

题意

岛上有说真话的好人和说假话的坏人,给你这两种人的人数。再给出q次问答结果,问答的格式是向a询问b是否是好人,回答是yes或者no。问是否可以分辨出全部好人,是的话打印清单

思路

这一篇是带权并查集解法,如果想看拓展并查集解法请戳这里

首先处理关系部分需要用到带权并查集,初次接触带权并查集的话请戳这里,按照那篇题解中的思路来看这篇题解~。我们将value设置为与父节点关系,0代表同类,1代表不同。那么初始化va数组为0,在find函数按轶合并部分我们用^ 异或上父节点value更新(具体可以手推一下),在unite部分如果ab同类,合并后的value就是va[a] ^ va[b] ^ 0,不同就是va[a] ^ va[b] ^ 1。(怎么来的可以按我贴的连接画图推一下,或者参考别的讲解带权并查集的博客)。

第一步完成后,我们拥有了并查集管理的多个独立块,在每一个集合中,权值为0的和权值为1的可以划分成两个子集合,我们的任务就是从每一个集合中选取一个子集,最终让这些子集元素数和为好人人数,且这样的选择唯一,那么用01背包跑一下就可以了。

代码

#include<iostream>
#include<vector>
#include<cstring>  
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
	typedef long long ll;
	const int maxn=2050;
	const int inf=0x3f3f3f3f;
	int fa[maxn];//父节点 
	int va[maxn];
	int q,n,m;
	int dp[1050][1050];//dp数组 
	bool ans[1050];//打印答案 
	bool b[1050];//集合去重 
	vector<int>v[maxn]; //暂时记录不同集合包含的点 
	vector<int>v1[maxn];//v1v2存放每个集合的子集 
	vector<int>v2[maxn];//
	void init(){
    
    
		for(int i=0;i<maxn;i++){
    
    
			fa[i]=i;
			va[i]=0;
		}
		memset(dp,0,sizeof(dp));
		memset(b,0,sizeof(b));
		memset(ans,0,sizeof(ans));
		for(int i=0;i<maxn;i++){
    
    
			v[i].clear();v1[i].clear();v2[i].clear();
		}
	}
	int find(int x){
    
    
		if(fa[x]==x)
			return x;
		int t=find(fa[x]);
		va[x]^=va[fa[x]]; 
		return fa[x]=t;
	}
	void unite(int x,int y,int t){
    
    
		int xr=find(x),yr=find(y);
		if(xr==yr){
    
    
			return ;
		}
		fa[xr]=yr;
		va[xr]=va[x]^va[y]^t;
	}
	int main(){
    
    
		IOS
		while(cin>>q>>n>>m){
    
    
			if(!q&&!n&&!m)
				break;
			init();
			int sm=n+m;
			while(q--){
    
    
				int a,b;
				string s;
				cin>>a>>b>>s;
				if(s=="yes"){
    
    
					unite(a,b,0);
				}
				else{
    
    
					unite(a,b,1);
				}
			}
			if(n==m){
    
    
				cout<<"no"<<endl;
				continue;
			}
			
			for(int i=1;i<=sm;i++){
    
    
				int x=find(i);
				v[x].push_back(i);
			}
			int t=1;
			for(int i=1;i<=2*sm;i++){
    
    
				bool bl=0;
				int le=v[i].size();
				for(int j=0;j<le;j++){
    
    
					int tmp=v[i][j];
					if(va[tmp]==0){
    
    
						v1[t].push_back(tmp);
						bl=1;
					}
					else{
    
    
						v2[t].push_back(tmp);
						bl=1;
					}
				}
				if(bl)
					t++;
			}
			//dp部分 
			dp[0][0]=1;
			for(int i=1;i<t;i++){
    
    
				int s1=v1[i].size(),s2=v2[i].size();
				for(int j=n;j>=s1;j--)
					dp[i][j]+=dp[i-1][j-s1];
				for(int j=n;j>=s2;j--)
					dp[i][j]+=dp[i-1][j-s2];
			}
			if(dp[t-1][n]!=1)
				cout<<"no"<<endl;
			else{
    
    
				int tmp=n;
				for(int i=t-1;i>=1;i--){
    
    
					int s1=v1[i].size(),s2=v2[i].size();
					if(dp[i-1][tmp-s1]==1){
    
    
						for(int j=0;j<s1;j++){
    
    
							//cout<<v1[i][j]<<endl;
							ans[v1[i][j]]=1;
						}
						tmp-=s1;
					}
					else{
    
    
						for(int j=0;j<s2;j++){
    
    
							//cout<<v1[i][j]<<endl;
							ans[v2[i][j]]=1;
						}
						tmp-=s2;
					}
				}
				for(int i=1;i<=sm;i++){
    
    
					if(!ans[i])
						continue;
					cout<<i<<endl;
				}
				cout<<"end"<<endl;
			}
			
		}
		
		return 0;
	} 

猜你喜欢

转载自blog.csdn.net/TheSunspot/article/details/107916866