zoukankan      html  css  js  c++  java
  • UVA 10158 并查集的经典应用

    这个题目一看就是用并查集,有N个国家代表,在M行给出两两之间的关系,敌人或者朋友,(当然如果该关系跟已知关系冲突,则输出-1)

    关系的几个约束条件时这样的

    在朋友方面,朋友的朋友就是自己的朋友,这个就是并查集。

    在敌人方面,

    x和其所有朋友的敌人都是敌人。

    x和其所有敌人的敌人都是朋友。

    主要是这个敌人的状态不太好表示,不是一个并查集能做到的,我一开始犯糊涂,直接用个图把x的敌人存贮起来,但是因为每次交新的朋友或者敌人,就要搜索全图,而且要把自己的敌人圈更新到整个朋友圈,这样不仅难以实现,复杂度也是相当高

    后来就发现一个神级方法,简单易用,即,每个国家都有自己的对立面(实际上不存在),作用是这样的,x的对立面为x+n,如果某国y要跟x做敌人,则,y就和x+n放在同一个并查集里。这样就不会跟本体有影响,但是又达到了结仇的目的。

    这样的话,x和y结盟,则 x和y属于一个集合, x+n和y+n属于同一集合(把对立面也绑定,再结仇的时候无论是和x还是y结仇,都会同时跟两个国家结仇,这样就其实就达到了第一个条件)。

    如果x和y结仇,则x和y+n在同一集合,同时,y和x+n在同一集合。(结仇之后,如果两个国家有共同的仇人,则通过并查集操作,必定到了同一个集合,这样就满足了仇人的仇人是朋友的条件)

    这样判断前后是否冲突,也可以根据这几个条件,如果当前操作是结仇,则一旦发现x和y已经是一个集合(或者他们的对立面),则冲突

    如果当前是结盟,一旦发现x和y+n是一个集合  或者 y和x+n是一个集合,则冲突。

    #include <iostream>
    #include <cstdio>
    #define N 10010
    using namespace std;
    int f[2*N];
    int n,x,y,c;
    void init()
    {
        for (int i=0;i<=2*n;i++){
            f[i]=i;
        }
    }
    int findset(int a)
    {
        if (a!=f[a])
            f[a]=findset(f[a]);
        return f[a];
    }
    
    void solve()
    {
        int r1,r2,r3,r4;
        r1=findset(x);
        r2=findset(y);
        r3=findset(x+n);
        r4=findset(y+n);
        //cout<<r1<<" "<<r2<<" "<<r3<<" "<<r4<<endl;
        if (c==1)
        {
            if (r1==r4){
                puts("-1");
                return;
            }
            f[r1]=r2;
            f[r3]=r4;
            return;
        }
        if (c==2)
        {
            if (r1==r2)
            {
                puts("-1");
                return;
            }
            f[r1]=r4;
            f[r2]=r3;
            return;
        }
        if (c==3)
        {
            if (r1==r2)
                puts("1");
            else
                puts("0");
        }
        if(c==4)
        {
            if (r1==r4)
                puts("1");
            else
                puts("0");
        }
    
    }
    int main()
    {
        int i,j;
        scanf("%d",&n);
        init();
        while (scanf("%d%d%d",&c,&x,&y))
        {
            if (!c) break;
            solve();
        }
        return 0;
    }
  • 相关阅读:
    解决VM 安装Ubuntu64与 Device/Credential Guard 不兼容,显示不支持64位系统
    WPF处理内容溢出
    .NET Standard 2.0 是什麼?可以吃嗎?
    C#.Net]启动外部程序的几种常用方法汇总
    在C#中接收系统屏幕锁定和解锁的事件
    C#.Net]启动外部程序的几种常用方法汇总
    MongoDB索引的使用
    读取xml并将节点保存到Excal
    开学后的第一篇
    续并查集学习笔记——Gang团伙题解
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3527173.html
Copyright © 2011-2022 走看看