等式 清华OJ 训练营 c++实现

等式

描述
有n个变量和m个“相等”或“不相等”的约束条件,请你判定是否存在一种赋值方案满足所有m个约束条件。

输入
第一行一个整数T,表示数据组数。

接下来会有T组数据,对于每组数据:

第一行是两个整数n,m,表示变量个数和约束条件的个数。

接下来m行,每行三个整数a,b,e,表示第a个变量和第b个变量的关系:

若e=0则表示第a个变量不等于第b个变量;
若e=1则表示第a个变量等于第b个变量
输出
输出T行,第i行表示第i组数据的答案。若第i组数据存在一种方案则输出"Yes";否则输出"No"(不包括引号)。

输入样例1
2
5 5
1 2 1
2 3 1
3 4 1
1 4 1
2 5 0
3 3
1 2 1
2 3 1
1 3 0
输出样例1
Yes
No
样例1解释
一共有2组数据。

对于第一组数据,有5个约束:

变量1=变量2
变量2=变量3
变量3=变量4
变量1=变量4
变量2≠变量5
显然我们可以令:

变量1=变量2=变量3=变量4=任意一个数值
变量5=任意一个和变量2不同的数值
故第一组数据输出"Yes"。 对于第二组数据,有3个约束:

变量1=变量2
变量2=变量3
变量1≠变量3
由前两个约束可推出变量1=变量3,但第三个约束表明变量1≠变量3,矛盾。

故第二组数据输出"No"。

输入样例2
点击下载

限制
对于10%的数据,n,m ≤ 5,T ≤ 5;

对于50%的数据,n,m ≤ 1000,T ≤ 10;

对于100%的数据,1 ≤ n ≤ 300000, m ≤ 500000,1 ≤ a,b ≤ n,T ≤ 100。

保证所有数据的n总和与m总和不超过500000。

时间:2 sec

空间:256 MB

提示
[用并查集来维护相等的集合。]

[改编自 NOI 2015 day1 T1 程序自动分析]

//#include <bits/stdc++.h>
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

// 并查集
// ================= 代码实现开始 =================

const int N = 300005;

//Father:每个节点的父亲节点
//Rank:节点的秩
int Father[N],Rank[N];

//查找节点x所在集合的根
//x:节点x
//返回值:根
int find(int x){
    return Father[x]==x ? x : Father[x]=find(Father[x]);
}

// 给定n个变量以及m个约束,判定是否能找出一种赋值方案满足这m个约束条件
// n:如题意
// m:如题意
// A:大小为m的数组,表示m条约束中的a
// B:大小为m的数组,表示m条约束中的b
// E:大小为m的数组,表示m条约束中的e
// 返回值:若能找出一种方案,返回"Yes";否则返回"No"(不包括引号)。
string getAnswer(int n, int m, vector<int> A, vector<int> B, vector<int> E) {
    //初始化
    for(int i = 1; i <= n; ++i){
        Father[i] = i;
        Rank[i] = 0;
    }

    //需要提前操作e=1的数据,将e=1的数据交换至前面
    int cnt=0;
    for(int i = 0; i < m; ++i)
        if(E[i] == 1){
            swap(A[i],A[cnt]);
            swap(B[i],B[cnt]);
            swap(E[i],E[cnt]);
            cnt++;
        }
    

    for(int i = 0; i < m; ++i){
        int setA = find(A[i]); //找到A[i]所在集合的根
        int setB = find(B[i]); //找到B[i]所在集合的根
        if(E[i] == 0){
            if(setA == setB){
                return "No";
            }
        }else{
            if(setA != setB){  //需要合并的情况
                //在union时,把rank更高的父节点作为根节点,规定rankB更高一些。
                if(Rank[setA]>Rank[setB])
                    swap(setA,setB); // 交换A,B集合
                Father[setA] = setB;
                if(Rank[setA] == Rank[setB])
                        Rank[setB]+=1;
            }
        }
    }
    return "Yes";
}

// ================= 代码实现结束 =================

int main() {
    int T;
    for (scanf("%d", &T); T--; ) {
        int n, m;
        scanf("%d%d", &n, &m);
        vector<int> A, B, E;
        for (int i = 0; i < m; ++i) {
            int a, b, e;
            scanf("%d%d%d", &a, &b, &e);
            A.push_back(a);
            B.push_back(b);
            E.push_back(e);
        }
        printf("%s\n", getAnswer(n, m, A, B, E).c_str());
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值