题目链接
题意
岛上有说真话的好人和说假话的坏人,给你这两种人的人数。再给出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;
}