在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设 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 1≤t≤105),表示需要判定的问题个数,注意这些问题之间是相互独立的。
对于每个问题,包含若干行:
第一行包含一个正整数 n n n ( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1≤n≤105),表示该问题中需要被满足的约束条件个数。
接下来 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 1≤i,j≤109,0≤e≤1),描述 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 ∑n≤2.1∗105
输出文件包括 t t t 行。
输出文件的第 k k k 行输出一个字符串 YES
或者 NO
,YES
表示输入中的第 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
#include
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();
}
}