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

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

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

题意:

        给你N个点(编号从0到N-1)和M个关系,要你判断这个图的所有点的顺序是否可以全部确定.不过对于任意点的关系可能存在A>B或A<B或A=B三种情况,如果A=B的话,那么就比较他们的编号,编号大的点分数大.(如果A=B且A编号>B编号,那么A分数大).

分析:

        输出结果有三种情况:缺少信息(即可拓扑排序),OK(能全排列),冲突(产生有向环).

        我们先按普通拓扑排序的过程来处理.

        假设A>B或B<A,直接在A与B之间加一条有向边即可.

        假设A=B呢?由于A与B的序号不同,所以我们最终还是可以在A与B间加一条有向边.

        但是如果接下来又出现C=A呢?不仅C与A之间要加边,C与B之间也要加边的.甚至C与和A相等的所有点之间都要加边.

        如果我们能做到上面的步骤,这个图就建立出来了.不过这个图就太复杂了.其实我们可以这么想,对于A=B=C=…=E这些(Rating相等但编号不等)都相等的边,他们之间肯定能分出确定的顺序的(因为它们编号肯定都不同).

        重点来了:我们把所有具有=关系的点都聚合成1(合并为一个连通分量,且只用该分量的根节点代替分量中的所有点).然后我们只处理><关系(添加两个分量之间的有向边).然后我们只需要判断这个缩减的图是否能全排序拓扑排序冲突即可.

        如何判断一个有向图是否能全排序或拓扑排序或冲突呢?按照topo()函数的处理过程来.如果在队列从Q队列出去的0入度点==所有连通分量的根数目,那么这个图肯定没冲突.

        假设没冲突,且在队列Q中始终只有1个元素的话,说明该图可全排序.否则只能拓扑排序.

        注意:题目中还有一个问题,我们必须保证所有具有=关系的点不冲突.(即不会出现A=B,之后又来一个A>B关系)这点在代码中有体现.

AC代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=10000+10;
const int maxm=20000+10;
int n,m;
int fa[maxn];
int find(int i)
{
    if(fa[i]==-1) return i;
    return fa[i]=find(fa[i]);
}
int in[maxn],head[maxn];
struct Edge
{
    int to,next;
}edges[maxm];   //边从0保存
int tot;        //计数边总数
void add_edge(int from,int to)
{
    edges[tot]=(Edge){to,head[from]};
    head[from]=tot++;
    in[to]++;
}
int topo()
{
    queue<int> Q;
    int fa_num=0;   //连通分量根数目
    int cnt=0;      //入度为0且从Q出队列的点数
    bool all_order=true;//是否可全排序标志
    for(int i=0;i<n;i++)if(find(i)==i)
    {
        fa_num++;
        if(in[i]==0) Q.push(i);
    }
    while(!Q.empty())
    {
        if(Q.size()>1) all_order=false;
        int u=Q.front(); Q.pop();
        cnt++;
        for(int e=head[u];e!=-1;e=edges[e].next)
        {
            int v=edges[e].to;
            if(--in[find(v)]==0)
                Q.push(find(v));
        }
    }
    if(cnt<fa_num) return -1;   //冲突
    if(all_order) return 1;     //可全排序
    return 0;                   //可拓扑排序
}
char str[maxm][5];
int a[maxm],b[maxm];
int main()
{
    while(scanf("%d%d%*c",&n,&m)==2)
    {

        memset(head,-1,sizeof(head));
        memset(fa,-1,sizeof(fa));
        memset(in,0,sizeof(in));
        tot=0;
        int ans=0;              //保存结果状态,-1表冲突,0表拓扑排序,1表全排序
        for(int i=0;i<m;i++)    //读取关系,合并连通分量
        {
            scanf("%d%s%d",&a[i],str[i],&b[i]);
            if(str[i][0]=='=')
            {
                int u=a[i], v=b[i];
                u=find(u), v=find(v);
                if(u!=v) fa[u]=v;
            }
        }
        for(int i=0;i<m;i++)    //添加有向边,且判断连通分量中是否冲突
        {
            int u=a[i], v=b[i];
            if(find(u)==find(v) && str[i][0]!='=')
            {
                ans=-1;         //冲突
                break;
            }
            if(str[i][0]=='<')      add_edge(find(v),find(u));
            else if(str[i][0]=='>') add_edge(find(u),find(v));
        }
        if(ans==0) ans=topo();
        if(ans==-1) printf("CONFLICT\n");
        else if(ans==0) printf("UNCERTAIN\n");
        else if(ans==1) printf("OK\n");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值