hdu1811 Rank of Tetris (并查集+拓扑排序)

4 篇文章 0 订阅
4 篇文章 0 订阅

题意:有n个人,以0~n-1编号。现在给出m个形如a>b,a=b,a<b的关系。现在将这些人按所给关系排序,(对于a=b的情况,a,b按照编号大小,大的排在前,即等号自身不会引发矛盾),不要求得到排序结果,要求得出能否排序,若不能,判断是产生了矛盾还是信息不完全。矛盾指形如a>b,b>c,c>a或者a=b,a>c,c>b这样的情况,信息不完全指无法按照当前给定结果判断出唯一顺序(没有加等号的并不会按照编号大小决定顺序)比如有a,b,c三个人,但是给的顺序只有a>b,a>c,这样就无法确定b,c谁先谁后。对于同时出现矛盾和信息不完全,输出矛盾。

思路:这题的思路是并查集加拓扑排序,并查集起到一个预处理掉等号的作用,再使用拓扑排序的性质判断两种错误。

如果不考虑带等号的情况,那么矛盾就相当于图中出现了有向环,对于拓扑排序来说,如果出现了环的话就是出现一部分点永远有入度,不会被排出来,那么每当一个点出队时对一个计数器加一,看看最后计数器的数目根总点数是否一样,如果不一样,就是出现了有向环。继续不考虑带等号的情况,如果在拓扑排序中,某次出现了多于一个点入度为零,那么这几个点的拓扑序就是任意的,就是没有确定顺序。

并查集在这题的引入目的就在于把带等号的情况转换为不带等号的情况。由于这道题不要求输出最终结果,那么如果两个点之间原先就不联通,带上等号以后就可以看做一个点了。如果两个点联通,那么这两个点在并查集上面的编号是相同的。对于前一种情况,我们也让这两个点在并查集上面的值相同(即一个指向另一个),但是因为当做缩点,要将之前说的计数器的值加一。这两种情况我们都使他们在并查集上面值相同,而第二个与第一个的区别在于没有更新计数器,这个目的是使判断环的操作留到拓扑排序的过程中。

这道题mle了我一天,怎么都找不到错哪了,最后发现是并查集的锅

如果这么写并查集的查找是对的

int findfa(int vt)
{
    if(vt!=fa[vt]) fa[vt]=findfa(fa[vt]);
    return fa[vt];
}
如果这么写,最后的内存会从2kkb飙到5~6wkb,而且还是没执行完的情况

int findfa(int vt)
{
    if(vt!=fa[vt]) fa[vt]=findfa(fa[vt]);
    return vt;
}
但是如果再加一句又会ac

int findfa(int vt)
{
    if(vt!=fa[vt]) fa[vt]=findfa(fa[vt]);
    if(vt==fa[vt])
    return vt;
}
不太懂编译器的蜜汁优化



#include <bits/stdc++.h>

using namespace std;
const int maxn=10005;
int fa[maxn];

int findfa(int vt)
{
    if(vt!=fa[vt]) fa[vt]=findfa(fa[vt]);
    return fa[vt];
}
struct edge
{
    int from,to;
    char op;
}ed[maxn*2];

int main()
{

    int v,e,s,t,in[maxn];
    vector<int> adjl[maxn];
    while(~scanf("%d%d",&v,&e))
    {
        for(int i=0;i<v;i++)fa[i]=i;
        memset(ed,0,sizeof(ed));
        memset(adjl,0,sizeof(adjl));
        memset(in,0,sizeof(in));
        char c;
        int sum=v;
        for(int i=0;i<e;i++)
        {
            scanf("%d %c %d",&ed[i].from,&ed[i].op,&ed[i].to);
            int x=findfa(ed[i].from),y=findfa(ed[i].to);
            if(ed[i].op=='='&&x!=y)
            {
                sum--;
                fa[y]=x;
            }

        }
        //cout<<fa[1]<<" "<<fa[2];
        for(int i=0;i<e;i++)
        {
            if(ed[i].op=='=')continue;
            int x=findfa(ed[i].from),y=findfa(ed[i].to);

             if(ed[i].op=='>')
            {
                adjl[x].push_back(y);
                in[y]++;
            }
            else{adjl[y].push_back(x);in[x]++;}
        }
        queue<int>q;
        for(int i=0;i<v;i++)
        {
            if(in[i]==0&&fa[i]==i)
            {
                q.push(i);
            }
        }

       // cout<<endl<<"\\"<<q.empty()<<endl;
        bool flag=false;
        while(!q.empty())
        {
            if(q.size()>1)flag=true;
            int vt=q.front();
            q.pop();
            sum--;//cout<<"  !"<<sum<<"!  ";
            for(int i=0;i<adjl[vt].size();i++)
            {
                in[adjl[vt][i]]--;
                if(in[adjl[vt][i]]==0)q.push(adjl[vt][i]);
            }
        }

        if(sum>0)cout<<"CONFLICT"<<endl;
        else if(flag)cout<<"UNCERTAIN"<<endl;
        else cout<<"OK"<<endl;
       // cout<<sum;

    }



    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值