拓扑排序

    今天,跟同学在NYOJ上做了一道搜索题,原先一直是以错误的思路来思考该问题。最后,问了别的同学后才知道原来是拓扑排序这个高级货。

  按照白书上所介绍的拓扑排序是有一下定义:

  假设有n个变量,还有m个二元组(u,v),分别表示变量u小于v。那么,所有变量从小到大排列起来应该是什么样子的呢?例如有4个变量a,b,c,d,若以知a<b,c<d,d<c,则这4个变量的排序可能是a<d<c<b。尽管还有可能其他的可能,你只需找出其中的一个即可。

  分析:

     把每个变量看成一个点,“小于”关系看成有向边,则我们得到了一个有向图。这样,我们的任务实际上是把一个图的所有结点排序,使得每一条有向边(u,v)对应的u都排在v的前面。在图论中,这个问题称为拓扑排序

  不难发现:如果图中存在有向环,则不存在拓扑排序,反之则存在。我们把不包含有有向环的有向图称为有向无环图。可以借助dfs函数完成拓扑排序:在访问完一个结点之后把它加入到当前拓扑序的首部(想想为什么不是尾部)

    (哈哈,个人见解是因为当dfs搜索时是先搜索到儿子结点的,所以先加入topo数组的是儿子结点,可以结合后面的程序加以理解)

 

 

const int MAXN = SIZEOF;
int t,n;
int c[MAXN];
int topo[MAXN],G[MAXN][MAXN];
bool DFS(int u)
{
    c[u] = -1;                    //正在栈中
    for(int v = 0;v < n;v++)if(G[u][v])
    {
        if(c[v]<0)return false;   //存在有向环,失败退出
        else if(!c[v] && !dfs(v))return false;
    }
    c[u] = 1;topo[--t] = u;
    return true;
}
bool TopoSort()
{
    t = n;
    memset(c,0,sizeof(c));
    for(int v = 0;v < n;v++)if(!c[v])
      if(!DFS(v))return false;
    return true;
}

 

    这里用到了一个c数组,c[u] = 0表示从来没有访问过(从来没有调用过dfs(u);c[u]=1表示已经访问过,并且还递归的访问过它的所有孙子(即dfs(u)曾被调用过,并已返回));c[u]=-1表示正在访问(即递归调用dfs(u)正在栈帧里,尚未返回)。     

   有向环:G[1][3],G[3][1],G[1][1]等都称为有向环

HDU 确定比赛名次

最简单的拓扑排序,数目很小,直接用矩阵。后来做的题发现有很多都不能用矩阵表示,要用邻接表或者链表来表示,主要是先找deg[i]为0的,他就排在前面,然后以他为起点,跟他连接的点的deg[]--,如此下去,排名是按分数优先,分数相同的按字典顺序排,先贴这道题的代码。

#include <stdio.h>
#include <string.h>
const int MAX = 502;
int n,m;
int topo[MAX],degree[MAX],G[MAX][MAX];
void TopoSort()
{
    int p = -1;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)if(!degree[j])
        {
            topo[i] = p = j;
            degree[j]--;
            break;
        }
        for(int j = 1;j <= n;j++)if(G[p][j])
        {
            G[p][j] = 0;
            degree[j]--;
        }
    }
}
void Print()
{
    for(int i = 1;i < n;i++)
      printf("%d ",topo[i]);
    printf("%d\n",topo[n]);
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int x,y;
        memset(G,0,sizeof(G));
        memset(degree,0,sizeof(degree));
        for(int i = 0;i < m;i++)
        {
            scanf("%d%d",&x,&y);
            if(!G[x][y])
            {
                G[x][y] = 1;
                degree[y]++;
            }
        }
        TopoSort();
        Print();
    }
    return 0;
}


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值