审题:
本题需要我们判断是否可以同时满足题目给定的若干等式或不等式,判断出后根据结果输出YES或NO
思路:
方法一:离散化+并查集使用并查集:其实题目中只存在两者相等或不等两种情况,而等于具有传递性,相等就可以放入同一个集合中用树结构存储,判断不等是否成立就可以看两者是否处于同一集合中,若在同一集合说明两者是相等的,此时不等一定不成立,直接返回false。所有情况都通过就返回true
数据范围图示:
注意:我们看到数据值的范围是可以到达1e9的,但是我们的fa数组无法开辟那么多空间,而数据个数只有1e5的规模,所以我们可以通过离散化数据值来存储进入fa数组,从而避免fa数组空间不足的情况出现
解题:
#include<iostream> #include<algorithm> #include<unordered_map> using namespace std; const int N = 1e5 + 10; int t,n; //成组记录,可以保留所有操作指令 struct node {int i, j, e; }a[N]; //离散化 int pos; int disc[N * 2]; unordered_map<int, int> m; //并查集 int fa[N * 2]; int find(int b) {return fa[b] == b ? b : fa[b] = find(fa[b]); } void un(int b, int c) {fa[find(b)] = find(c); } bool judge(int b, int c) {return find(b) == find(c); } //解决函数 bool solve() {//清空痕迹pos = 0;m.clear();//离散化操作cin >> n;for (int i = 1; i <= n; i++){cin >> a[i].i >> a[i].j >> a[i].e;disc[++pos] = a[i].i;disc[++pos] = a[i].j;}sort(disc + 1, disc + 1 + pos);//升序排序int num = 0;for (int i = 1; i <= pos; i++){if (m.count(disc[i])) continue;//去重num++;m[disc[i]] = num;}//并查集操作//初始化for (int i = 1; i <= pos; i++){fa[i] = i;}for (int i = 1; i <= n; i++){if (a[i].e == 1)//合并{un(m[a[i].i], m[a[i].j]);}}for (int i = 1; i <= n; i++){//检查判断if (a[i].e == 0 && judge(m[a[i].i], m[a[i].j])){return false;}}return true; } int main() {cin >> t;while (t--){if (solve()) cout << "YES" << endl;else cout << "NO" << endl;}return 0; }
注意:
1、由于我们的等式不等式有多组,所以我们使用结构体数组记录数据,且这样记录还会将等式/不等式两边的对象和符号绑定起来,方便将数据离散化后可以直接知道哪些数之间有什么关系
2、由于需要判断多组,所以我们需要将前面的残留数据清除,disc直接用数据覆盖即可,pos变量和m哈希表需要手动清除数据,fa则每次都进行初始化无需特殊处理。
3.在进行等式和不等式的并查集处理的时候不能放在一起只扫描一次,因为题目中的等于和不等于不一定是连续的,也就是说等于和不等于是掺杂在一起的,若一轮处理会导致判误
举例
x1 = x2
x2 ≠ x3
x2 = x3
如果我们按顺序合在一起扫描等式和不等式,那么在第二条地方不会返回false,因为x2和x3此时可以不等,且进入第三条我们也只是将集合合并,并未涉及判断,最后会返回true
但是实际上等式是不成立的,所以我们应该先扫描一次所有等式,然后再扫描所有不等式进行判断
错误代码:
P1955 [NOI2015] 程序自动分析 - 洛谷