• HDU1811 拓扑排序判环+并查集


    HDU Rank of Tetris

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1811

    题意:中文问题就不解释题意了。

    这道题其实就是一个拓扑排序判圈,我的博客里面其他几篇拓扑排序判圈的套路一样。但是这道题与他们不同的的是在大小关系里面存在一种 “=”的关系,这就意味的那些序号不同的点,实际上是一个点。共享入度和出度。我们可以通过并查集将他们合并,合成一个点。这里说一下如何判断信息不完全。我们早先在做拓扑排序,多种排列方式的时候,按照字典序输出。我们建立一个优先队列,维护字典序,这个时候堆里是有很多元素的,我们通过优先队列取出最小或者最大,实际上这些元素在拓扑排序中是等价关系,也就是说他们之间的大小关系无法比较。在这道题中,如果滞留在队列中的元素超过一个说明有两个元素无法比较的,就说明了答案是UNCLEARED。直接看代码吧!,具体细节写在注释里面了。

    //Author: xiaowuga
    #include <bits/stdc++.h>
    #define maxx INT_MAX
    #define minn INT_MIN
    #define inf 0x3f3f3f3f
    const long long N=10000+10;
    using namespace std;
    typedef long long LL;
    vector<int>p[N];
    int f[N];
    int in[N];
    struct node{
        int x,y;
        char ch;
    }oj[N];
     int n,m;
    int ct=0,flag=0;
    //并查集套路
    int Find(int x){
        return f[x]==x?x:f[x]=Find(f[x]);
    }
    void topo(){
        queue<int>q;
        while(!q.empty()) q.pop();
        for(int i=0;i<n;i++) if(!in[i]&&Find(i)==i) q.push(i);//首先得是并查集中的根,然后出度为0
        while(!q.empty()){
            if(q.size()>1) flag=1;//同级元素大于一个说明有至少有两个元素无法比较,所以信息不完全
            int t=q.front();q.pop();
            ct++;
            for(int i=0;i<p[t].size();i++){
                int tmp=p[t][i];
                if(--in[tmp]==0) q.push(tmp);
            }
        }
        if(ct!=n) cout<<"CONFLICT"<<endl;
        else if(flag) cout<<"UNCERTAIN"<<endl;
        else cout<<"OK"<<endl;
    }
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);
        while(cin>>n>>m){
            for(int i=0;i<n;i++) f[i]=i;
            memset(in,0,sizeof(in));
            for(int i=0;i<n;i++) p[i].clear();
            int x,y;
            ct=flag=0;
            //先处理'='带来的点的减少,建立并查集缩点
            for(int i=0;i<m;i++){
                cin>>oj[i].x>>oj[i].ch>>oj[i].y;
                if(oj[i].ch=='='){
                    x=Find(oj[i].x);
                    y=Find(oj[i].y);
                    if(x!=y) {f[x]=y;ct++;}//缩点了别忘了ct++,因为相当于总点数减少了一个
                }
            }
            //根据缩完点的点边关系,反向建图(正向建图也可以)
            for(int i=0;i<m;i++){
                if(oj[i].ch=='=') continue;
                x=Find(oj[i].x);
                y=Find(oj[i].y);
                if(oj[i].ch=='>'){
                    p[y].push_back(x);     
                    in[x]++;
                }
                else{
                    p[x].push_back(y);
                    in[y]++;
                }
                
            }
            //并查集处理完建图之后就是常规的拓扑排序
            topo();
        }
        return 0;
    }

    在最小生成树的克鲁斯卡尔算法里,并查集被用来判断是否形成环路,正如我在我的其他几份博文里面说的那样,我认为在一个图中,如果出现环,那么环可以缩成一个点。环上的点实际上是等价的,也就是说并查集实际上是通过边的关系,说明两个不同的点,合并成一个点,通过一个根共享他们的信息。a=b全等价于a>b,a<b这样两条边。我们根据这个信息把a,b合并。通过一个根可以是a,也可以是b来查询这个整体的信息。总结一句并查集就是把一些不同的东西合并在一起,一起查询的工具。

  • 相关阅读:
    求一个数的阶乘在 m 进制下末尾 0 的个数
    区间dp
    最长公共子序列变形
    学习stm32专区
    C/C++中static关键字详解
    ASP.NET调用Office Com组件权限设置
    TreeView控件
    SQL笔记(1)索引/触发器
    NPOI 1.2.5 教程
    SQL Povit
  • 原文地址:https://www.cnblogs.com/xiaowuga/p/7220492.html
Copyright © 2020-2023  润新知