• 题解 [NOI2015]程序自动分析


    据说考前写题解可以$ ext{RP}$++?

    这题还是算一道并查集水题了吧qwq我又做了好久

    --------------------------------------------------------

    题目描述

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

    考虑一个约束满足问题的简化版本:假设$x_1$,$x_2$,$x_3$...代表程序中出现的变量,给定$ ext{n}$个形如$x_i=x_j$或$x_i≠x_j$的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为:$x_1=x_2$,$x_2=x_3$,$x_3=x_4$,$x_4≠x_1$,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。

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

    输入格式

    输入文件的第$1$行包含$1$个正整数$ ext{t}$,表示需要判定的问题个数。注意这些问题之间是相互独立的。

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

    第$1$行包含$1$个正整数$ ext{n}$,表示该问题中需要被满足的约束条件个数。接下来$ ext{n}$行,每行包括$3$个整数$ ext{i,j,e}$,描述$1$个相等/不等的约束条件,相邻整数之间用单个空格隔开。若$ ext{e=1}$,则该约束条件为$x_i=x_j$;若$ ext{e=0}$,则该约束条件为$x_i≠x_j$;

    输出格式

    输出文件包括$ ext{t}$行。

    输出文件的第 $ ext{k}$行输出一个字符串“$ ext{YES}$” 或者“$ ext{NO}$”(不包含引号,字母全部大写),“$ ext{YES}$” 表示输入中的第$ ext{k}$个问题判定为可以被满足,“$ ext{NO}$” 表示不可被满足。

    ------------------------------------------------------------

    题目要求判断多个等式和不等式是否矛盾。

    由于多个等式具有传递性,所以会想到用并查集来维护变量之间的关系。

    如果当前数据无解,那么显然是出现了多个等式间接满足$ ext{x=y}$,但是题目中又出现$ ext{x≠y}$的条件。

    所以,对于每一组$ ext{x=y}$的约束条件,合并这两个元素,代表这两个元素满足相等关系。

    如果出现$ ext{x≠y}$的约束条件,检查这两个元素是否在并查集中,如果是,就输出无解;否则继续判断,直到判断完所有的条件。

    中间过程有一些细节处理需要注意一下:(也是自己遇到的问题,当然这里有可能是我没有想到用什么更好的办法去处理)

    $ ext{1.}$ 题目数据范围中的$ ext{i,j}$很大,数组肯定装不下,所以可以离散化处理;

    $ ext{2.}$ 我们的程序中,如果有两个条件互相约束且满足一定的顺序(比如说第一行$ ext{x=y}$,第二行$ ext{x≠y}$),那么我们的程序还可以正常输出;但是如果将这两行交换一下输入顺序,程序就会输出错误的答案。一开始我想的是另外一个并查集来维护不等式之间的关系,但是后来发现不等式没有传递性...所以我们可以考虑优先处理变量相等的关系,然后再来处理变量不相等的关系,这样就可以保证查询不相等变量是否连通时不会受到后面关系的影响。

    概括一下就是:要优先处理相等变量之间的关系,保证不会影响后面不相等变量之间的关系。

    $ ext{3.}$ 由于有$ ext{n}$对关系,每对关系又有$ ext{2}$个变量,所以并查集数组的大小一定要开两倍!

    上代码~

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    
    int t, n, tot, cnt;
    int fa[N << 1], a[N << 1], temp[N << 1];
    
    struct node {
    	int x, y;
    	bool z;
    } ask[N];//存储查询 
    
    inline int get (int x) {
    	if (x == fa[x]) return x;
    	return fa[x] = get(fa[x]);
    }
    
    inline void merge (int x, int y) {
    	fa[get(x)] = get(y);
    }
    
    inline bool tmp (node g, node h) {
    	return g.z > h.z;
    }
    
    inline void solve () {
    	scanf("%d", &n);
    	tot = cnt = 0;
    	for (int i = 1; i <= n * 2; i++) fa[i] = i;//数组大小一定要开两倍啊 
    	for (int i = 1; i <= n; i++) {
    		scanf("%d%d%d", &ask[i].x, &ask[i].y, &ask[i].z);
    		temp[++tot] = ask[i].x;
    		temp[++tot] = ask[i].y;
    	}
    	sort(temp + 1, temp + tot + 1);
    	sort(ask + 1, ask + n + 1, tmp);//对查询排序,保证优先处理相等变量之间的关系 
    	a[++cnt] = temp[1];
    	for (int i = 2; i <= tot; i++) {
    		if (temp[i] != temp[i - 1]) a[++cnt] = temp[i];
    	}//离散化输入的变量 
    	for (int i = 1; i <= n; i++) {
    		int xx = lower_bound(a + 1, a + cnt + 1, ask[i].x) - a;
    		int yy = lower_bound(a + 1, a + cnt + 1, ask[i].y) - a;
    		if (ask[i].z) merge(xx, yy); 
    		else {
    			if (get(xx) == get(yy)) {
    				printf("NO
    ");
    				return;
    			}//如果前面满足相等关系但是这里又满足不等关系,无解 
    		}
    	}
    	printf("YES
    ");
    }
    
    int main () {
    	scanf("%d", &t);
    	while (t--) solve();
    	return 0;
    }
    
    创作不易,转载请注明出处!
  • 相关阅读:
    Linux 常用命令
    去除重叠区间
    Python 小工具之大文件去重
    有趣的pyfiglet
    杨辉三角
    Paginator分页
    Linux用户和用户组
    Grub介绍
    Linux系统运行级别
    Linux系统启动流程
  • 原文地址:https://www.cnblogs.com/66ccffxym/p/11862887.html
Copyright © 2020-2023  润新知