第三章 图论 No.13拓扑排序

这篇具有很好参考价值的文章主要介绍了第三章 图论 No.13拓扑排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


拓扑序和DAG有向无环图联系在一起,通常用于最短/长路的线性求解
第三章 图论 No.13拓扑排序,AcWing算法提高课 课程记录,图论

裸题:1191. 家谱树

1191. 家谱树 - AcWing题库
第三章 图论 No.13拓扑排序,AcWing算法提高课 课程记录,图论

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110, M = 10010;
int h[N], e[M], ne[M], idx;
int d[N], q[N], hh, tt = -1;
int n, m;

void add(int x, int y)
{
    e[idx] = y, ne[idx] = h[x], h[x] = idx ++ ;
}

void topsort()
{
    for (int i = 1; i <= n; ++ i )
        if (!d[i]) q[ ++ tt ] = i;
    while (tt >= hh )
    {
        int x = q[hh ++ ];
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (-- d[y] == 0) q[++ tt] = y;
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d", &n);
    for (int x = 1; x <= n; ++ x )
    {
        int y;
        while (scanf("%d", &y), y)
        {
            add(x, y);
            d[y] ++ ;
        }
    }
    
    topsort();
    for (int i = 0; i <= tt; ++ i )
        printf("%d ", q[i]);
    return 0;
}

差分约束+拓扑排序:1192. 奖金

1192. 奖金 - AcWing题库
第三章 图论 No.13拓扑排序,AcWing算法提高课 课程记录,图论

由于图中所有边权都是正数,可以直接使用topsort求解差分约束问题
根据题意,要求一个最小值,使用最长路求解,转化题目的条件: A > = B + 1 A >= B + 1 A>=B+1 x i > = x 0 + 100 x_i >= x_0 + 100 xi>=x0+100
x 0 x_0 x0为一个虚拟源点,向每个点连了一条权值为100的边

若图中存在环,topsort的队列长度将小于n,因为环的起点无法进入队列
先用topsort判断图中是否存在环,若不存在,根据拓扑序遍历图,求解最长路

#include <iostream>
#include <cstring>
using namespace std;

const int N = 10010, M = 30010;
int h[N], e[M], ne[M], w[M], idx;
int q[N], d[N], hh, tt = -1;
int dis[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool topsort()
{
    q[ ++ tt ] = 0;
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        for (int i = h[x]; i != -1; i = ne[i] )
        {
            int y = e[i];
            if ( -- d[y] == 0) q[ ++ tt ] = y;
        }
    }
    return tt == n;
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++ i )
    {
        int x, y;
        scanf("%d%d", &x, &y);
        add(y, x, 1);
        d[x] ++ ;
    }
    
    for (int i = 1; i <= n; ++ i ) add(0, i, 100), d[i] ++ ;
    if (!topsort()) puts("Poor Xed");
    else 
    {
        for (int k = 0; k <= tt; ++ k )
        {
            int x = q[k];
            for (int i = h[x]; i != -1; i = ne[i])
            {
                int y = e[i];
                dis[y] = max(dis[y], dis[x] + w[i]);
            }
        }
        int sum = 0;
        for (int i = 1; i <= n; ++ i ) sum += dis[i];
        printf("%d\n", sum);
    }
    
    return 0;
}

debug:最后的遍历没有按照拓扑序

for (int x = 0; x <= tt; ++ x )
{
	for (int i = h[x]; i != -1; i = ne[i])
	{
		int y = e[i];
		dis[y] = max(dis[y], dis[x] + w[i]);
	}
}

集合+拓扑序:164. 可达性统计

[164. 可达性统计 - AcWing题库](https://www.acwing.com/problem/content/description/166/
第三章 图论 No.13拓扑排序,AcWing算法提高课 课程记录,图论

从集合的角度思考, f ( i ) f(i) f(i)表示i这个点能到达的所有点,i首先能到达自己,其次能达到 f ( j 1 ) , f ( j 2 ) , . . . , f ( j n ) f(j_1), f(j_2), ... , f(j_n) f(j1),f(j2),...,f(jn),假设i与n个点直接相连
那么要求 f ( i ) f(i) f(i),就必须求出 f ( j 1 ) , f ( j 2 ) , . . . , f ( j n ) f(j_1), f(j_2), ... , f(j_n) f(j1),f(j2),...,f(jn),即拓扑排序中位于i之后的所有点的 f ( j ) f(j) f(j)
所以这题先拓扑排序,再根据拓扑排序的逆序,求 f ( i ) f(i) f(i)
如何表示集合 f ( i ) f(i) f(i)?用STL的容器bitset,假设图中有N个点,那么bitset的长度为N,每个点都用一个bitset记录其集合,1表示i能递达这个点,0表示不能递达
那么 f ( i ) = f ( j 1 ) ∩ f ( j 2 ) ∩ . . . ∩ f ( j n ) f(i) = f(j_1)∩ f(j_2)∩ ...∩f(j_n) f(i)=f(j1)f(j2)...f(jn)

关于bitset的使用,bitset之间支持|=运算,count()输出bitset中1的个数

#include <iostream>
#include <cstring>
#include <bitset>
using namespace std;

const int N = 30010, M = N;
int h[N], e[M], ne[M], idx;
int q[N], d[N], hh, tt = -1;
bitset<N> f[N];
int n, m;

void add(int x, int y)
{
    e[idx] = y, ne[idx] = h[x], h[x] = idx ++ ;
}

void topsort()
{
    for (int i = 1; i <= n; ++ i )
        if (!d[i]) q[ ++ tt ] = i;
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if ( -- d[y] == 0) q[ ++ tt ] = y;
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; ++ i )
    {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y);
        d[y] ++ ;
    }
    topsort();
    
    for (int i = tt; i >= 0; -- i )
    {
        int x = q[i];
        f[x][x] = 1;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            f[x] |= f[y];
        }
    }
    for (int i = 1; i <= n; ++ i ) printf("%d\n", f[i].count());

    return 0;
}

差分约束+拓扑序:456. 车站分级

456. 车站分级 - AcWing题库
第三章 图论 No.13拓扑排序,AcWing算法提高课 课程记录,图论

分析题意:对于每一条路线,未经过的站点的等级一定小于经过的站点等级,并且最低的站点等级为1级
题目要求所有等级划分中的最少等级数,用最长路求最小值。将以上条件转换成差分约束中的两个条件: B > = A + 1 B >= A + 1 B>=A+1, x i > = x 0 + 1 x_i >= x_0 + 1 xi>=x0+1
x 0 x_0 x0为虚拟源点,通过 x 0 x_0 x0能到达图中的所有点,那么就一定能递达所有边

由于每条路线路都会建立n * m条边,极端情况下可能会爆空间,所以考虑如何优化
一条路径中未经过的站点将向经过的站点连接一条权值为1的边,一共n * m条,由于这些边的权值相同,可以在这些边中创建一个虚拟点v,未经过的点分别向v连一条权值为0的边,v向经过的点分别连接一条权值为1的边。这样,从未经过的点到经过的点的权值和依然为1,但是需要建立的边数为n + m,此时的边数在极端情况下也不会爆空间

#include <iostream>
#include <cstring>
using namespace std;

const int N = 2010, M = 1e6 + 10;
int h[N], e[M], ne[M], w[M], idx;
int d[N], q[N], hh, tt = -1;
bool st[N]; int dis[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void topsort()
{
    for (int i = 1; i <= n + m; ++ i ) 
        if (!d[i]) q[ ++ tt ] = i;
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (-- d[y] == 0) q[ ++ tt ] = y;
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++ i )
    {
        memset(st, false, sizeof(st));
        int t, start = n, end = 0;
        scanf("%d", &t);
        while (t -- )
        {
            int x;
            scanf("%d", &x);
            st[x] = true;
            start = min(start, x), end = max(end, x);
        }
        int v = n + i;
        for (int i = start; i <= end; ++ i )
        {
            if (st[i]) add(v, i, 1), d[i] ++ ;
            else add(i, v, 0), d[v] ++ ;
        }
    }
    
    topsort();
    for (int i = 1; i <= n; ++ i ) dis[i] = 1;
    for (int i = 1; i <= tt; ++ i )
    {
        int x = q[i];
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            dis[y] = max(dis[y], dis[x] + w[i]);
        }
    }
    int res = 0;
    for (int i = 1; i <= n; ++ i ) res = max(res, dis[i]);
    printf("%d\n", res);
    
    return 0;
}

debug:w[M]写成了w[N],又是这样,然后debug了半天,了文章来源地址https://www.toymoban.com/news/detail-650546.html

到了这里,关于第三章 图论 No.13拓扑排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第三章 图论 No.10无向图的双连通分量

    无向图有两种双连通分量 边双连通分量,e-DCC 点双连通分量,v-DCC 桥:删除这条无向边后,图变得不连通,这条边被称为桥 边双连通分量:极大的不含有桥的连通区域,说明无论删除e-DCC中的哪条边,e-DCC依旧连通 (该连通分量的任意边属于原图中的某条环)。此外,任意两

    2024年02月12日
    浏览(40)
  • 第三章 图论 No.11二分图,匈牙利算法与点覆盖

    257. 关押罪犯 - AcWing题库 最大最小问题,一眼二分 答案的范围在 [ 1 , 1 e 9 ] [1, 1e9] [ 1 , 1 e 9 ] 之间,二分答案,check(mid) check:将所有权值大于mid的边进行二分,若能得到二分图,返回true,否则返回false 最终将得到最优解ans,所有大于ans的边组成的图为二分图,若是图中有边

    2024年02月12日
    浏览(42)
  • 第三章 图论 No.8最近公共祖先lca, tarjan与次小生成树

    O ( m l o g n ) O(mlogn) O ( m l o g n ) ,n为节点数量,m为询问次数,lca是一种在线处理询问的算法 自己也是自己的祖先 倍增: f a ( i , j ) fa(i, j) f a ( i , j ) 表示从i开始,向上走 2 j 2^j 2 j 步走到的点 j = 0,走到父节点 j 0,分两步走,先走到 2 j − 1 2^{j-1} 2 j − 1 步再走 2 j − 1 2^{

    2024年02月13日
    浏览(44)
  • 第三章 图论 No.6负环之01分数规划与特殊建图方式

    裸题:904. 虫洞 904. 虫洞 - AcWing题库 这个==真的服,调半天,还有,邻接表的大小又设置错了 01分数规划:361. 观光奶牛 361. 观光奶牛 - AcWing题库 在图论问题中,所有形如:某个部分之和除以某个部分之和最大的问题,被称为01分数规划,通常使用二分解决这类问题 根据题意

    2024年02月13日
    浏览(41)
  • 第三章 图论 No.9有向图的强连通与半连通分量

    连通分量是无向图的概念,yxc说错了,不要被误导 强连通分量:在一个有向图中,对于分量中的任意两点u,v,一定能从u走到v,且能从v走到u。强连通分量是一些点的集合,若加入其他点,强连通分量中的任意两点就不能互相递达 半连通分量:在一个有向图中,对于分量中

    2024年02月13日
    浏览(51)
  • 第三章 图论 No.5最小生成树之虚拟源点,完全图与次小生成树

    虚拟源点:1146. 新的开始 1146. 新的开始 - AcWing题库 与一般的最小生成树问题不同,本题需要在建立电站的电井之间建立电网,在两个电站之间建立电网需要花费金额,可以看成一条具有权值的边 但是建立电网的前提是:其中一个电井需要建立电站,建立电站也需要费用 已经

    2024年02月14日
    浏览(40)
  • 第三章 图论 No.3 flody之多源汇最短路,传递闭包,最小环与倍增

    flody的四个应用: 多源汇最短路 传递闭包 找最小环 恰好经过k条边的最短路 倍增 多源汇最短路:1125. 牛的旅行 1125. 牛的旅行 - AcWing题库 直径概念:同一连通块中,两个距离最远的点之间的距离 如何求直径?由于图中存在着两个连通块,所以直接对全图做一个flody,就能更

    2024年02月14日
    浏览(45)
  • 第三章 图论 No.2单源最短路之虚拟源点,状压最短路与最短路次短路条数

    dp是特殊的最短路,是无环图(拓扑图)上的最短路问题 1137. 选择最佳线路 1137. 选择最佳线路 - AcWing题库 对于每组测试数据,该重置的数据要重置,我没有重置idx,导致TLE 处理反向建图,还有一种扩展做法:虚拟源点 设置虚拟源点,与每个起点之间连接边权为0的边 原问题

    2024年02月14日
    浏览(49)
  • 第三章 搜索与图论(二)(最短路)

    1、对于稠密图,由于朴素版的dijkstra算法与边数无关使用这种算法的复杂度较低。稀疏图用堆优化版的算法;单源最短路中存在负权边用SPFA 算法通常较好;多源用floyd算法;  难点:如何建图,抽象为最短路问题。 由于稠密图用这种算法,邻接矩阵存图,注意把g初始化为

    2024年02月21日
    浏览(46)
  • 第三章 搜索与图论(二)——最短路问题

    源点表示起点,汇点表示终点 一些认识: m和 n 2 n^2 n 2 一个级别是稠密图,m和n一个级别是稀疏图 最短路问题不区分有向图与无向图,因为无向图是一种特殊的有向图,能解决有向图的最短路问题,就能解决无向图的最短路问题 起点确定,终点是除起点外的其他点 默认n表示

    2024年02月13日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包