AcWing 237. 程序自动分析(第二类离散化 并查集)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

输入若干对 两未知数xixj 相等不等 的约束条件,判断输入的约束条件是否矛盾。

举例:

输入一组约束条件:x1=x2,x2=x3,x3=x4,x1≠x4 ,则显然这组约束条件是存在矛盾的,因为前三个可以推出x1=x4,而第四个条件x1≠x4与之矛盾。

思路:

分析一下上面的举例,我们可以发现这组约束条件中“相等”的约束条件可以看做是一个并查集合并的过程,如x1=x2,相当于是将x1x2合并到一个集合的操作,而“不等”的约束条件,如x1≠x4相当于是在说x1x4不属于一个集合。

首先,对于约束条件的配置顺序我们是不关心的,换句话说,顺序不会影响我们最终的结果,因此我们可以先考虑相等的情况:xi=xj(这些情况当然不可能有矛盾),再考虑不等的情况:xi!=xj,如果根据之前相等的情况已经可以推出xi=xj,即xixj两者已经在同一集合中了,则表明有矛盾。

值得注意的地方:

题中的变量范围非常的大,达到1e9,我们要将数据范围缩小,因为我们最多只有有1e6个限制条件,每个限制条件中最多只有2个变量,所以:虽然我们总共的变量数量为1e9,但是我们需要用到的只有2e6,所以第一步我们要进行离散化(题目设置范围很大,需要用到的很小,将所有要用到的变量映射到 值 较小的区域里去):1e9->2e6

离散化有两种写法:

第一种是保序:离散化前是什么大小关系,离散化后还是什么大小关系(排序、判重、二分,可用库函数来实现)。

第二种不要求保序(由于不需要排序等操作,会比第一种好写,且代码量会少很多):可以用 map(每次在map中查询一下这个值是否存在,如果存在则返回对应的值,否则对应另一个值)或 hash表(即unordered_map或手写hash表,运用方式和map相同)。

步骤:

①离散化。
②将所有相等条件合并(union)。
③依次判断每个不等条件(query)。

代码细节:

①根据题意,xixj相等的情况用e=1表示,不等的情况用e=0表示,因此我们可以在讲所有数据输入到结构体数组时,按照e从大到小来排列,这样我们在后续循环判断的过程中就可以先处理相等的情况后处理不等的情况。

对于结构体排序,我们是用重载小于号(结构体中默认重载小于号),但由于我们要从大到小排列,因此我们可以对重载小于号的返回值作一下改动。

struct Query
{
        int xi, xj;
        int e;
        bool operator< (const Query &x) const{
                return (x.e < e);//原本如果从小到大排序我们要写成return (e < x.e);
        }
}query[N];

②离散化核心操作:将范围是1e9的值映射到范围是2e6的值。

unordered_map<int, int> umi;
int idx = 0;
int get(int x)
{
        if(!umi.count(x)) return umi[x] = ++idx;//如果未出现过则开辟映射一个新的值(从1开始)
        return umi[x];//出现过则直接返回之前映射过的值
}

时间复杂度:nlogn

代码:

#include<bits/stdc++.h>

using namespace std;
int t;
int n;
const int N = 2e6+10;
struct Query
{
        int xi, xj;
        int e;
        bool operator< (const Query &x) const{
                return (x.e < e);
        }
}query[N];
unordered_map<int, int> umi;
int p[N];
int idx;

int get(int x)
{
        if(!umi.count(x)) return umi[x] = ++idx;
        return umi[x];
}

void init(int n)
{
        for(int i=1;i<=n;++i) p[i] = i;
}

int find(int x)
{
        if(p[x]!=x) p[x] = find(p[x]);
        return p[x];
}

void _union(int a, int b)
{
        int pa = find(a), pb = find(b);
        if(pa!=pb) p[pa] = pb;
}

int main()
{
        cin>>t;
        while(t--)
        {
                idx = 0;
                umi.clear();
                cin>>n;
                for(int i=1;i<=n;++i)//①离散化
                {
                        int x, y;
                        scanf("%d%d%d",&x, &y, &query[i].e);
                        query[i].xi = get(x), query[i].xj = get(y);
                }
                sort(query+1, query+n+1);
                init(idx);
                bool ok = false;
                for(int i=1;i<=n;++i)
                {
                        if(query[i].e==1) _union(query[i].xi, query[i].xj);//②将所有相等条件合并(union)。
                        else//③判断每个不等条件(query)
                        {
                                if(find(query[i].xi)==find(query[i].xj))
                                {
                                        ok = true;
                                        break;
                                }
                        }

                }
                if(ok) puts("NO");
                else puts("YES");
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值