Acwing-237程序自动分析(并查集+离散化)+acwing-238 银河英雄传说 (带权并查集)

Acwing-237,程序自动分析(并查集+离散化)

题意:并查集判断是否有矛盾;

解:i,j 范围在1e9,但是n范围在1e6,所以总共数据范围在2e6内;

离散化处理下输入数据,接下来就是裸的并查集;

先将相等的进行合并

如果不等的之中有哪两个的父节点相同,那么就有冲突;

注意:unordered_map会ac,但是使用map则会超时,

unordered_map比map的速度要快一点

另:可以使用另外一个数组储存,去重之后进行离散化。

优缺点对比:

map:

优点:

有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作
红黑树,内部实现一个红黑树使得map的很多操作在logn的时间复杂度下就可以实现,因此效率非常的高
缺点: 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

适用处:对于那些有顺序要求的问题,用map会更高效一些

unordered_map:

优点: 因为内部实现了哈希表,因此其查找速度非常的快
缺点: 哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
总结:

内存占有率的问题就转化成红黑树 VS hash表 , 还是unorder_map占用的内存要高。
但是unordered_map执行效率要比map高很多
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的

代码:

#include<bits/stdc++.h>
const int maxn=1e6+10;
typedef long long ll;
using namespace std;

int t,tot;
int fa[maxn];
struct node{
    int x,y,e;
}N[maxn];
unordered_map<int,int> mp;

int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int get(int x)//离散化处理
{
    if(mp.count(x)==0) mp[x]=++tot;
    return mp[x];
}

int main()
{
    cin>>t;
    while(t--)
    {
        int n;
        tot=0;
        mp.clear();
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            int x,y,e;
            cin>>x>>y>>e;
            N[i]={get(x),get(y),e};
        }

        for(int i=1; i<=tot; i++) fa[i]=i;

        bool flag=0;

        for(int i=1; i<=n; i++)//合并所有相等的
        {
            if(N[i].e==1){
            int fx=find(N[i].x),fy=find(N[i].y);
            if(fx!=fy)
             fa[fx]=fy;}
        }

        for(int i=1; i<=n; i++)
        {
            if(N[i].e==0)//判断是否有矛盾
            {
                int fx=find(N[i].x),fy=find(N[i].y);
                if(fx==fy)
                {
                    flag=1;
                    break;
                }
            }
        }

        if(flag) cout<<"NO
";
        else cout<<"YES
";
    }
    system("pause");
    return 0;
}

acwing-238 银河英雄传说 (带权并查集)

题意:t条指令,两种操作,一种是合并,另一种查询子节点到父节点之间的距离。

解:查询距离,用一个数组储存距离d【maxn】;

维护每个战舰到队头的距离,这样如果两只战舰在同一列上且i != j,那么我们就直接通过两个战舰各自到队头的距离相减得到答案,直接暴力维护的话,复杂度会特别大。

在路径压缩时候,当前节点到根节点之间的距离要相加d【a】+=d【fa【x】】,沿途一直相加,不断更新子节点直接连接父节点,直到路径压缩完毕。

但是当一个队接到另一个队的时候,这个队的队头就排到了另一个队的队尾,那么我们就推算出这个队头到另一个队头的距离数值上等于另一个队的长度,此时就需要知道另一个队的长度,设为l【maxn】。

维护队长l[maxn]即为两队长度相加...

合并时先更新d【】,再更新l【】,最后查询即可。

初始化,d为0,l为1

并查集:路径压缩+合并+查找+初始化

代码:

#include<bits/stdc++.h>
const int maxn=1e6+10;
typedef long long ll;
using namespace std;

int t;
int fa[maxn];
int d[maxn];
int l[maxn];//队的长度;
int find(int x)
{
    if(x==fa[x])
     return x;
    int root=find(fa[x]);//找根节点
    d[x]+=d[fa[x]];//递归的叠加距离
    fa[x]=root;//把子节点连接到祖宗节点上
    return root;
}

bool isok(int a,int b )//判断
{return find(a)==find(b); }

void Union(int a,int b)//合并
{
    if(!isok(a,b))
    {
        int root_a,root_b;
        root_a=find(a);
        root_b=find(b);
        d[root_a]=l[root_b];
        l[root_b]+=l[root_a];
        fa[root_a]=root_b;
    }
}

int quary(int a,int b)//查询
{
    if(a==b) return 0;
    if(isok(a,b)) return abs(d[a]-d[b])-1;

    return -1;
}
void init()//初始化
{
    for(int i=1; i<=maxn-10; i++)
    {
        fa[i]=i;
        l[i]=1;
    }
}
int main()
{
    init();
    int m;
    cin>>m;
    while(m--)
    {
        char ch;
        int x,y;
        cin>>ch>>x>>y;
        if(ch=='M') Union(x,y);
        else cout<<quary(x,y)<<endl;
    }
    system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/sweetlittlebaby/p/13436051.html