The Number of Imposters(并查集+dfs)

题目链接:Problem - D - Codeforces The Number of Imposters

n个人,一共有m个描述,每个表述为i认为j是好人或者是坏人,求可能的最大的坏人数。考虑并查集,并查集里面有一个典中典的题,叫食物链,类似的,利用并查集来判断是否满足条件。对于每一个人i,并查集f[i]和f[i+n]分别表示与 i是同类和异类的集合。建图参考如下代码。如果i认为j是好人那么i与j的类型是相同的,反正是不同的。如果满足条件所有的n个人会被分成若干个连通块,单独的考虑每一个连通块,按照好人与坏人中的极大值进行加权最终可计算出答案。

#include<bits/stdc++.h>
const int N=2e5+5;
using namespace std;
int f[N<<1],T,n,m,cnt=0,sum=0;
bool vs[N<<1];
vector<int> g[N<<1];
int find(int x){
    if(f[x]==x) return x;
    return f[x]=find(f[x]);
}
void merge(int u,int v){
    if(find(u)!=find(v))
    f[find(u)]=find(v);
}
int dfs(int fa,int u){
    if(vs[u]) return 0;
    vs[u]=true;
    if(u>n) cnt++;
    int res=1;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==fa) continue;
        res+=dfs(u,v);
    }
    return res;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=2*n;i++){//初始化
            f[i]=i,vs[i]=false;
            g[i].clear();
        }
        int flag=false;
        for(int i=1,x,y;i<=m;i++){
            char s[15];
            scanf("%d%d %s",&x,&y,s);
            if(s[0]=='i'){
                merge(x+n,y);
                merge(x,y+n);
                g[x].push_back(y+n);//建图
                g[y+n].push_back(x);
                g[y].push_back(x+n);
                g[x+n].push_back(y);
            }
            else {
                merge(x,y);
                merge(x+n,y+n);
                g[x].push_back(y);//建图
                g[y].push_back(x);
                g[x+n].push_back(y+n);
                g[y+n].push_back(x+n);
            }
        }
        for(int i=1;i<=n;i++){
            if(find(i)==find(i+n)){
                flag=true;
                break;
            }
        }
        int ans=0;
        if(flag) printf("-1\n");
        else{
            for(int i=1;i<=n;i++){
                if(!vs[i]&&!vs[i+n]){
                    cnt=0,sum=dfs(0,i);
                    ans+=max(cnt,sum-cnt);
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值