NO.93十六届蓝桥杯备战|图论基础-拓扑排序|有向无环图|AOV网|摄像头|最大食物链计数|杂物(C++)

有向⽆环图

若⼀个有向图中不存在回路,则称为有向⽆环图(directed acycline graph),简称 DAG 图

AOV⽹

举⼀个现实中的例⼦:课程的学习是有优先次序的,如果规划不当会严重影响学习效果。课程间的先后次序可以⽤有向图表⽰

在这种有向图中,⽤顶点表⽰活动,⽤有向边< v i , v j v_{i}, v_{j} vi,vj>表⽰活动 v i v_{i} vi必须先于活动 v j v_{j} vj进⾏,这种有向图叫做顶点表⽰活动的⽹络(Activity On Vertex Network),简称 AOV ⽹

AOV⽹中不能有回路,否则就不能确定回路中的活动究竟哪个先实施。因此⼀个可⾏的AOV⽹必须是有向⽆环图

拓扑排序

拓扑排序的⽬标是将有向⽆环图中的所有结点排序,使得排在前⾯的结点不能依赖于排在后⾯的结点。在课程问题中,相当于就是找到⼀个排课的合法顺序。具体流程可借助队列进⾏:

  1. 将图中所有⼊度为0的点,加⼊到队列中;
  2. 取出队头元素,删除与该点相连的边。如果删除之后的后继结点的⼊度变为0,加⼊到队列中;
  3. 重复2操作,直到图中没有点或者没有⼊度为0的点为⽌。
    拓扑排序判断是否有环:
    跑⼀遍拓扑排序算法,如果有结点没有进队,那么就表明有环
B3644 【模板】拓扑排序 / 家谱树 - 洛谷
c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 110;

int n;
vector<int> edges[N];
int in[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int j;
        while (cin >> j, j)
        {
            edges[i].push_back(j);
            in[j]++;
        }
    }

    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        if (in[i] == 0) q.push(i);        
    }

    while (q.size())
    {
        int x = q.front(); q.pop();
        cout << x << " ";

        //删除对应的边
        for (auto y : edges[x])
        {
            in[y]--;
            if (in[y] == 0) q.push(y);
        }
    }
    
    return 0;
}
P2712 摄像头 - 洛谷

拓扑排序判断是否有环。

直接跑⼀遍拓扑排序,然后统计⼀下有多少摄像头没有出队。那么这些没有出队的摄像头就是环⾥⾯的元素。

注意:

  • 有些位置可能没有摄像头,需要判断⼀下
c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 510;

int n;
vector<int> edges[N];
int in[N];
bool st[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int x, m, y; cin >> x >> m;
        st[x] = true;
        while (m--)
        {
            cin >> y;
            edges[x].push_back(y);
            in[y]++;
        }
    }

    queue<int> q;
    //入度为0的点加入队列
    for (int i = 0; i <= 500; i++)
    {
        if (st[i] && in[i] == 0) q.push(i);
    }

    while (q.size())
    {
        auto x = q.front(); q.pop();

        for (auto y : edges[x])
        {
            in[y]--;
            if (st[y] && in[y] == 0) q.push(y);
        }
    }

    int ret = 0;
    for (int i = 0; i <= 500; i++)
    {
        if (st[i] && in[i]) ret++;        
    }
    if (ret == 0) cout << "YES" << endl;
    else cout << ret << endl;
    
    return 0;
}
P4017 最大食物链计数 - 洛谷

拓扑排序的过程中,进⾏动态规划。

对于每⼀个节点i,通过它的路径为:前驱所有结点的路径总数之和。因此,可以在拓扑排序的过程中,维护从起点开始到达每⼀个节点的路径总数

c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 5010, MOD = 80112002;

int n, m;
vector<int> edges[N];
int in[N], out[N];
int f[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y; cin >> x >> y;
        edges[x].push_back(y);
        in[y]++, out[x]++;
    }

    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        if (in[i] == 0)
        {
            f[i] = 1;
            q.push(i);
        }
    }

    while (q.size())
    {
        auto x = q.front(); q.pop();

        for (auto y : edges[x])
        {
            f[y] = (f[y] + f[x]) % MOD;
            in[y]--;
            if (in[y] == 0) q.push(y);
        }
    }

    int ret = 0;
    for (int i = 1; i <= n; i++)
    {
        if (out[i] == 0)
        {
            ret = (ret + f[i]) % MOD;
        }
    }

    cout << ret << endl;
    
    return 0;
}
P1113 杂务 - 洛谷

拓扑排序的过程中,进⾏动态规划。

对于每⼀个事件i,完成它的最⼩时间为:完成前驱所有事件的最⼩时间中的最⼤值+当前事件的完成时间。因此,可以在拓扑排序的过程中,维护每⼀个事件完成的最⼩时间,然后更新当前事件的最⼩时间

c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 10010;

int n;
vector<int> edges[N];
int in[N], f[N];
int len[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int b,a;
        cin >> b >> len[b];
        while (cin >> a, a)
        {
            edges[a].push_back(b);
            in[b]++;
        }
    }

    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        if (in[i] == 0) q.push(i);     
    }

    int ret = 0;
    while (q.size())
    {
        int a = q.front(); q.pop();
        f[a] += len[a];
        ret = max(ret, f[a]);

        for (auto b : edges[a])
        {
            f[b] = max(f[b], f[a]);
            in[b]--;
            if (in[b] == 0) q.push(b);
        }
    }
    cout << ret << endl;
    
    return 0;
}
相关推荐
智者知已应修善业9 小时前
【51单片机89C51及74LS273、74LS244组成】2022-5-28
c++·经验分享·笔记·算法·51单片机
Byron Loong11 小时前
【c++】为什么有了dll和.h,还需要包含lib
java·开发语言·c++
坚果派·白晓明12 小时前
【鸿蒙PC三方库移植适配框架解读系列】第一篇:Lycium C/C++ 三方库适配 — 概述与环境配置
c语言·开发语言·c++·harmonyos·开源鸿蒙·三方库·c/c++三方库
咩咦13 小时前
C++学习笔记02:cin 和 cout 输入输出
c++·学习笔记·cin·输入输出·cout
咩咦13 小时前
C++学习笔记05:引用和常引用
c++·学习笔记·引用·const·常引用
香蕉鼠片13 小时前
算法过程中不会的
开发语言·c++
阿旭超级学得完13 小时前
C++11包装器(function和bind)
java·开发语言·c++·算法·哈希算法·散列表
li星野13 小时前
位运算 & 数学 & 高频进阶九题通关(Python + C++)
c++·python·学习·算法
hnjzsyjyj13 小时前
洛谷 T145300:这是一棵树吗? ← 图论握手定理
图论·握手定理
磊 子14 小时前
多态类原理+四种类型转换+异常处理
开发语言·c++·算法