• 拓扑排序


    基于贪心和基于dfs实现的拓扑排序

    拓扑排序的定义

    对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列(并非全序/线序),使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。通俗的说就是,一张有向无环图的拓扑排序可以使得任意的起点u,它的一个终点v,使得在序列中的顺序是u在前v在后

    NOTE:并不是所有图都存在拓扑排序,拓扑排序在图中也不是唯一的.
    不含环路的有向图必包含入度为零的顶点—>因此一定存在拓扑排序

    废话少说,直接实战:P1137:旅行计划



    思路:由于x城市要么在y东边,要么在其西边,所以这个图一定是一个有向无环图(Directed Acyclic Graph简称DAG)。
    记得DP需要满足什么原则吗?无后效性。如果不是在拓扑序中进行DP,会完全破坏无后效性。正是因为拓扑序u在前,v在后的性质,这才选择使用拓扑排序。

    这里我们选择用链式向前星来存储图:链式向前星

    Kahn算法(贪心)实现(拓扑排序+DP):

    #include <iostream>
    #include <cstdio>
    #include <queue>
    
    using namespace std;
    
    /*
        贪心实现拓扑排序的样例:P1137旅行计划
        为了保证dp的无后效性,先拓扑排序再dp
     先转化:只往东走,以第i个城市为终点最多能游览多少个城市 <==> 只往西走,以第i个城市为起点最多能浏览多少个城市
        转移方程(已拓扑排序后): dp[u] = max(dp[u], dp[v]+1),如果存在一条从u到v的边
     */
    
    queue<int>q1;
    
    typedef struct qianlao
    {
        int u[200001], v[200001], w[200001]; //从u到v
        int first[100001], next[200001];
        int indu[100001]; //统计入度来判断起始点
        int n, m;
    }list;  //创建符合题目的邻接表
    
    list G;
    int Topans[100001], t = 1;
    
    void Topsort(void);
    int main(void)
    {
        scanf("%d %d",&G.n, &G.m); //n个点,m个关系
        for(int i = 1; i <= G.n; i++)
        {
            G.first[i] = -1;
            G.indu[i] = 0;           //邻接表初始化
        }
        for(int i = 1; i <= G.m; i++)
        {
            G.next[i] = -1;          //邻接表初始化
        }
        for(int i = 1; i <= G.m; i++)
        {
            scanf("%d %d",&G.v[i], &G.u[i]); //注意这里逆向的读入DAG
            G.w[i] = 1;     //这里权值无用
            G.next[i] = G.first[G.u[i]];
            G.first[G.u[i]] = i;
            G.indu[G.v[i]]++;         //输入邻接表以及入度
        }
        Topsort(); //拓扑排序
        int dp[G.n+1]; //dp[i]表示以第i个城市为起点最多能浏览多少个城市
        for(int i = 1; i <= G.n; i++)
        {
            dp[i] = 1;
        }
        int k;
        for(int i = G.n; i >= 1; i--)   //进行简单dp
        {
            k = G.first[Topans[i]];
            while(k!=-1)
            {
                dp[G.u[k]] = max(dp[G.u[k]], dp[G.v[k]]+1);   //dp转移
                k = G.next[k];
            }
        }
        for(int i = 1; i <= G.n; i++)
        {
            printf("%d
    ",dp[i]);
        }
        return 0;
    }
    
    void Topsort(void)
    {
        int k;
        for(int i = 1; i <= G.n; i++)   //注意这里是拓扑排序的逆序顺序
            if(G.indu[i]==0)          //初始点(入度为0的点)入队
                q1.push(i);
        while(!q1.empty())
        {
            Topans[t] = q1.front();    //贪心的取出队列中的点到Topans中
            t++;
            q1.pop();        //出列
            k = G.first[Topans[t-1]];
            while(k!=-1)               //遍历所有由刚刚出列点指向的点
            {
                G.indu[G.v[k]]--;
                if(G.indu[G.v[k]]==0)   //如果删边后入度为0,则入队
                    q1.push(G.v[k]);
                k = G.next[k];
            }
        }
    }
    
    


    dfs实现(拓扑排序+DP):

  • 相关阅读:
    《Python编程从入门到实践》学习笔记<7>:用户输入和while循环
    《Python编程从入门到实践》学习笔记<6>:字典
    《Python编程从入门到实践》学习笔记<5>:IF语句
    Navicat12激活,版本v12.1.18
    将博客搬至CSDN
    SVN中文提示
    SQL Server行转列
    .net操作AD域
    当经历过,你成长了,自己知道就好
    Outlook2010 POP3方式连接Hotmail等邮箱的错误处理
  • 原文地址:https://www.cnblogs.com/SpicyArticle/p/11628905.html
Copyright © 2020-2023  润新知