[Daimayuan] 程序自动分析(C++,强连通分量)

题面

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。

考虑一个约束满足问题的简化版本:假设 x 1 , x 2 , x 3 , … x_1,x_2,x_3,… x1,x2,x3, 代表程序中出现的变量,给定 n n n 个形如 x i = x j x_i=x_j xi=xj x i ≠ x j x_i≠x_j xi=xj 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。

例如,一个问题中的约束条件为: x 1 = x 2 , x 2 = x 3 , x 3 = x 4 , x 1 ≠ x 4 x_1=x_2,x_2=x_3,x_3=x_4,x_1≠x_4 x1=x2,x2=x3,x3=x4,x1=x4 这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。

现在给出一些约束满足问题,请分别对它们进行判定。

输入格式

输入文件的第一行包含一个正整数 t t t ( 1 ≤ t ≤ 1 0 5 1≤t≤10^5 1t105),表示需要判定的问题个数,注意这些问题之间是相互独立的。

对于每个问题,包含若干行:

第一行包含一个正整数 n n n ( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105),表示该问题中需要被满足的约束条件个数。

接下来 n n n 行,每行包括 3 3 3 个整数 i , j , e i,j,e i,j,e ( 1 ≤ i , j ≤ 1 0 9 , 0 ≤ e ≤ 1 1≤i,j≤10^9,0≤e≤1 1i,j109,0e1),描述 1 1 1 个相等/不等的约束条件,相邻整数之间用单个空格隔开。若 e = 1 e=1 e=1,则该约束条件为 x i = x j x_i=x_j xi=xj;若 e = 0 e=0 e=0,则该约束条件为 x i ≠ x j x_i≠x_j xi=xj

∑ n ≤ 2.1 ∗ 1 0 5 ∑n≤2.1∗10^5 n2.1105

输出格式

输出文件包括 t t t 行。

输出文件的第 k k k 行输出一个字符串 YES 或者 NOYES 表示输入中的第 k k k 个问题判定为可以被满足,NO 表示不可被满足。

输入样例
2
2
1 2 1
1 2 0
2
1 2 1
2 1 1
输出样例
NO
YES
解题思路

等价关系具有传递性、对称性、自反性,具有等价关系的元素在一个强连通分量中。

所以采用并查集维护等价关系,对于可满足的程序,相等的元素在同一集合中,不相等的元素不在同一集合中。

实现过程中的问题:

1)索引取值范围过大,不能用数组维护:

采用map容器维护索引,提供一维数组的抽象;

...
for (int i = 0; i < n; i++) {
	cin >> u >> v >> w;
	fa.insert({ u,u });
	fa.insert({ v,v });
    ...
}
...
fa.clear();//每轮程序分析结束后,不要忘记清空

2)对于不相等关系的处理,需要等到所有等价关系都确定后再进行判断:

...
for (int i = 0; i < n; i++) {
	cin >> u >> v >> w;
	fa.insert({ u,u });
	fa.insert({ v,v });
    ...
    if (!w) edges[++idx] = { u,v };//先缓存
}
...
for (int i = 0; !flag && i < idx; i++) {//之后统一处理
	u = edges[idx].u; v = edges[idx].v;
	if (is_insame(u, v)) flag = true;
}
...
fa.clear();//每轮程序分析结束后,不要忘记清空

最后,AC代码如下:

#include <iostream>
#include <map>
using namespace std;
const int max_n = 2.1 * 1e5;
const int max_i = 1e9;

map<int, int> fa;
struct edge { int u, v; } edges[max_n];
int idx = 0;

int find(int x) {
    return x == fa[x] ? x : (fa[x] = find(fa[x]));
}

bool is_insame(int x, int y) {
    x = find(x); y = find(y);
    return x == y;
}

void merge(int x, int y) {
    x = find(x); y = find(y);
    fa[y] = x;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        int u, v, w;
        idx = 0;
        for (int i = 0; i < n; i++) {
            cin >> u >> v >> w;
            fa.insert({ u,u });
            fa.insert({ v,v });
            if (w && !is_insame(u, v)) merge(u, v);
            else if (!w) edges[idx++] = { u,v };
        }
        bool flag = false;
        for (int i = 0; !flag && i < idx; i++) {
            u = edges[i].u; v = edges[i].v;
            if (is_insame(u, v)) flag = true;
        }
        if (!flag) cout << "YES\n";
        else cout << "NO\n";
        fa.clear();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值