SCC-Tarjan,缩点问题

文章目录

    • 前言
    • 引例
    • 什么是缩点?
    • 缩点的应用
      • 一、合并强连通子图为强连通图
        • 题目描述
        • 输入/输出格式
        • 原题链接
        • 题目详解
      • 二、集合间偏序关系
        • 题目描述
        • 输入/输出格式
        • 原题链接
        • 题目详解
      • 三、最大点权和路径
        • 题目描述
        • 输入/输出格式
        • 原题链接
        • 题目详解
      • 其他OJ练习

前言

图论中的缩点问题通常是指在有向图中,通过将强连通分量内的所有节点缩成一个节点,从而简化图的结构,这个过程称为缩点。这样做可以帮助我们分析和解决一些实际问题。

阅读本文前如果不了解Tarjan算法求解强连通分量,建议先看:SCC-Tarjan算法,强连通分量算法,从dfs到Tarjan详解-CSDN博客

阅读本文前如果不了解拓扑排序,建议先看:拓扑排序详解-CSDN博客


引例

一位老板刚成立一家公司,招揽了数名员工,各司其职,分工合作,共谋富贵。随着公司良性运转,又陆续招了一批批员工,随着员工数目增加,他们的工作内容,职位关系也都变得复杂,老板只觉管理力不从心。后来他发现那些工作内容相同的员工之间都需要进行业务交流,而工作内容不同的员工之中只有少数几个leader之间会进行业务来往,他灵机一动,将公司成员划分到了不同的部门之中,以后处理业务只需要下发到相关的各个部门中不需要具体的各个员工都找来一遍。随后公司管理压力骤减,工作效率大大提升,蒸蒸日上……

上面将成员之间复杂的业务关系简化为了各个部门之间的业务关系,将具有相同工作内容的员工整合为了一个部门,这就是我们中缩点的应用。

什么是缩点?

在有向图中,将每个强连通分量中的所有节点缩成一个节点,称为缩点(Vertex Contraction),两个不同强连通分量节点间若存在连边则将两个缩点间添加一条有向边,这样我们就将原本的有向有环图转化为了有向无环图

缩点的应用

缩点显然降低了图的规模,可以降低我们分析问题的复杂程度,但是对于缩点后的图的性质的维护,需要我们慎重考虑。

我们下面通过例题来感受缩点的精妙之处。

一、合并强连通子图为强连通图

题目描述

共有 n 所学校 (1 <= n <= 10000) 已知他们实现设计好的网络共 m 条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。

输入/输出格式

输入
第一行一个正整数 n。
接下来 n 行每行有若干个整数,用空格隔开。
第 i+1 行,每行输入若干个非零整数 x,表示从 i 到 x 有一条线路。以 0 作为结束标志。

输出
第一行一个整数,表示至少选几所学校作为共享软件的母机,能使每所学校都可以用上。
第二行一个整数,表示至少要添加几条线路能使任意一所学校作为母机都可以使别的学校使用上软件。
1 <= n <= 10000,1 <= 边数 <= 50000

原题链接

[P2812 校园网络【USACO]Network of Schools加强版】 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目详解

显然学校间的网络通信关系构成了一张有向图,其中有若干强连通分量,先忽略第一问,我们发现第二问就是问转化为强连通图的最少添加边数。这个问题下显然很庞大,我们不妨利用缩点,将原先的有向有环图转化为有向无环图,在进行研究。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左上图为样例一的有向图表示,显然有三个强连通分量:{1,2,4},{3},{5}

右上图为缩点表示,得到了三个点{1,2,3}

那么我们回看问题,

**对于问题一:**我们观察缩点图,我们发现只需要两个母机,即1,3即可令所有学校可以用得上网络,那么如何得到一般情况下的解呢?

我们知道缩点图是一个有向无环图,换言之就是若干棵有向树组成的森林,那么每棵有向树的有一个根,根的数目就是母机的数目。而对于有向树的根,显然就是入度为0的点,则第一问的答案就是缩点图中入度为0点的数目。

**对于问题二:**问题二等价为将缩点图转化为强连通图,而对于强连通图每一个点入度出度都不为0,我们统计缩点图中入度为0的点sum1和出度为0的点sum2,那么需要添加的最小边数就是max(sum1,sum2)。注意可能输入样例给的就是强连通图,所以要特判。

AC代码如下:

#include <iostream>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <bitset>
using namespace std;
#define MOD 1000000007
#define N 10010
#define sc scanf
bitset<N> instk;
int dfn[N]{0}, low[N]{0}, st[N], head[N], scc[N], sz[N], cnt = 0, idx = 0, top = 0, tot = 0;
struct edge
{
    int v, nxt;
} edges[50010];
void addedge(int u, int v)
{
    edges[idx] = {v, head[u]};
    head[u] = idx++;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++tot;
    st[top++] = x, instk[x] = 1;
    for (int j = head[x]; ~j; j = edges[j].nxt)
    {
        int v = edges[j].v;
        if (!dfn[v])
        {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        }
        else if (instk[v])
        {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (low[x] == dfn[x])
    {
        int y;
        cnt++;
        do
        {
            y = st[--top];
            instk[y] = 0;
            scc[y] = cnt;
            sz[cnt]++;
        } while (x != y);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    memset(head, -1, sizeof(head));
    int n, x = 0, y = 0;
    cin >> n;
    for (int i = 1, a; i <= n; i++)
        while (cin >> a, a)
            addedge(i, a);

    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            tarjan(i);
    vector<int> in(cnt + 1), out(cnt + 1);
    for (int i = 1; i <= n; i++)
        for (int j = head[i]; ~j; j = edges[j].nxt)
            if (scc[i] != scc[edges[j].v])
            {
                in[scc[edges[j].v]]++, out[scc[i]]++;
            }
    for (int i = 1; i <= cnt; i++)
        x += !in[i], y += !out[i];
    cout << x << '\n';
    cout << (cnt != 1 ? max(x, y) : 0);
    return 0;
}

二、集合间偏序关系

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入/输出格式

输入

第一行:两个用空格分开的整数:N 和 M。
接下来 M 行:每行两个用空格分开的整数:A 和 B,表示 A 喜欢 B。

输出
一行单独一个整数,表示明星奶牛的数量。

原题链接

[P2341 USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目详解

显然明星牛就是所有牛到其自身都有路径可达的牛,那么如果明星牛不唯一的话,这些明星牛彼此之间互相可达自然在一个强连通分量内,所以我们还是在缩点图上研究问题。

这样我们就又得到了个有向无环图,那么对于明星牛集合的缩点,其必须满足出度为0,否则就不配被称为明星牛(bushi

其实如果学过离散数学或者抽象代数的话,明星牛就是偏序关系哈斯图中的最大上界,没学过也没关系,只要明白对于明星牛缩点,必然满足所有缩点到其都有路径,也就是说出度为0的缩点只有明星牛缩点一个,否则会出现有小黑子牛不喜欢明星牛(bushi

AC代码如下:

#include <iostream>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <bitset>
using namespace std;
#define MOD 1000000007
#define N 10010
#define sc scanf
bitset<N> instk;
int dfn[N]{0}, low[N]{0}, st[N], head[N], scc[N], sz[N], cnt = 0, idx = 0, top = 0, tot = 0;
struct edge
{
    int v, nxt;
} edges[50010];
void addedge(int u, int v)
{
    edges[idx] = {v, head[u]};
    head[u] = idx++;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++tot;
    st[top++] = x, instk[x] = 1;
    for (int j = head[x]; ~j; j = edges[j].nxt)
    {
        int v = edges[j].v;
        if (!dfn[v])
        {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        }
        else if (instk[v])
        {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (low[x] == dfn[x])
    {
        int y;
        cnt++;
        do
        {
            y = st[--top];
            instk[y] = 0;
            scc[y] = cnt;
            sz[cnt]++;
        } while (x != y);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    memset(head, -1, sizeof(head));
    int n, m, x = 0, y = 0;
    cin >> n >> m;
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;
        addedge(x, y);
    }

    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            tarjan(i);
    vector<int> out(cnt + 1);
    for (int i = 1; i <= n; i++)
        for (int j = head[i]; ~j; j = edges[j].nxt)
            if (scc[i] != scc[edges[j].v])
                out[scc[i]]++;
    int ans = 0, zero = 0;
    for (int i = 1; i <= cnt; i++)
        if (!out[i])
            ans = sz[i], zero++;
    cout << (zero <= 1 ? ans : 0);
    return 0;
}

三、最大点权和路径

题目描述

给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入/输出格式

输入
第一行两个正整数 n,m
第二行 n 个整数,其中第 i 个数 ai 表示点 i 的点权。
第三至 2m + 2 行,每行两个整数 u , v ,表示一条 u→v 的有向边。

输出
共一行,最大的点权之和。

原题链接

P3387 【模板】缩点 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目详解

对于求点权和问题,很多时候都可以转化为生成树问题,但是对于本题他要求的是最大点权路径,如果是有向无环图我们可以通过拓扑+动态规划来进行求解,但是本题要求是**“允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次”**,那么这样的话我们直接在原图上求解CPU会干烧的(精神上

那么怎么办呢?自然是缩点大法,我们缩点的权值就是集合内权值之和,那么我们就转化为了在缩点图这样一个有向无环图上求最大点权路径,那么我们用拓扑+动态规划进行求解即可。

但是我们注意到,我们的SCC是用Tarjan来求解的,而Tarjan是利用时间戳求解SCC即先访问的SCC其SCC编号反而大,那么我们按照编号从大到小访问缩点自然就是拓扑序了

以下图为例,我们发现节点编号从大到小来看刚好为拓扑序

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AC代码如下:

#include <iostream>
#include <cstring>
#include <vector>
#include <functional>
#include <algorithm>
#include <cmath>
#include <functional>
#include <climits>
#include <bitset>

using namespace std;
#define MOD 1000000007
#define N 10010
#define sc scanf
bitset<N> instk;
int dfn[N]{0}, low[N]{0}, dp[N]{0}, st[N], head[N], Head[N], scc[N], sz[N], cost[N], cnt = 0, idx = 0, Idx = 0, top = 0, tot = 0;
struct edge
{
    int v, nxt;
} edges[50010], Edges[50010];
void addedge(int u, int v)
{
    edges[idx] = {v, head[u]};
    head[u] = idx++;
}
void Addedge(int u, int v)
{
    Edges[Idx] = {v, Head[u]};
    Head[u] = Idx++;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++tot;
    st[top++] = x, instk[x] = 1;
    for (int j = head[x]; ~j; j = edges[j].nxt)
    {
        int v = edges[j].v;
        if (!dfn[v])
        {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        }
        else if (instk[v])
        {
            low[x] = min(low[x], dfn[v]);
        }
    }
    if (low[x] == dfn[x])
    {
        int y;
        cnt++;
        do
        {
            y = st[--top];
            instk[y] = 0;
            scc[y] = cnt;
            sz[cnt] += cost[y];
        } while (x != y);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    memset(head, -1, sizeof(head));
    memset(Head, -1, sizeof(Head));

    int n, m, x = 0, y = 0;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> cost[i];
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;
        addedge(x, y);
    }

    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            tarjan(i);

    for (int i = 1; i <= n; i++)
        for (int j = head[i]; ~j; j = edges[j].nxt)
            if (scc[i] != scc[edges[j].v])
                Addedge(scc[i], scc[edges[j].v]);
    int ans = 0;
    for (int i = cnt; i; i--)
    {
        if (!dp[i])
            ans = max(ans, dp[i] = sz[i]);
        for (int j = Head[i]; ~j; j = Edges[j].nxt)
            ans = max(ans, dp[Edges[j].v] = max(dp[Edges[j].v], dp[i] + sz[Edges[j].v]));
    }
    cout << ans;
    return 0;
}

其他OJ练习

P2002 消息扩散 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

P1262 间谍网络 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/254827.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL·8.0·源码】MySQL 语法树基础知识

基础 我们都知道 SQL 语句经过词法分析器时&#xff0c;识别扫描输入的 SQL 语句&#xff0c;将关键词、标识符、常量等分解转换成独立的 tokens&#xff0c;进一步在语法分析阶段根据语法规则检查 tokens 序列的结构并不断 shift 、reduce 构建成 SQL 语法解析树。 在 MySQL…

ubuntu18.04 64 位安装笔记——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

进入VirtuakBox官网&#xff0c;网址链接&#xff1a;Oracle VM VirtualBoxhttps://www.virtualbox.org/ 网页连接&#xff1a;Ubuntu Virtual Machine Images for VirtualBox and VMwarehttps://www.osboxes.org/ubuntu/ 将下发的ds_db01.sql数据库文件放置mysql中 12、编写S…

19 高速列车场景下3Gpp 5G NR的DMRS设计与评估

文章目录 解决问题设计DMRS仿真参数仿真结果 解决问题 多普勒/扩展影响十分显著&#xff0c;设计用于信道估计时&#xff0c;需要考虑解调参考信号&#xff0c;5G用DMRS结构而不是CRS结构&#xff0c;因此需要为高速UE设计DMRS结构&#xff0c;DMRS设计是为了提高信道估计并减…

jdk多版本切换环境变量管理(jdk1.8和jdk17)

jdk多版本切换环境变量管理&#xff08;jdk1.8和jdk17&#xff09; 看了很多网上的博客&#xff0c;根本都不行&#xff0c;我总结出来规律如下&#xff1a; 首先环境变量要配置成这个样子&#xff1a;这些博客都会教你们配 接着配什么classpath&#xff0c;看其他博客就行 还…

百度地图添加坐标点,并返回坐标信息

1、创建地图容器 在mounted中初始化地图、鼠标绘制工具和添加鼠标监听事件 vue data中添加地图和绘制工具对象 2、添加初始化化地图方法 initMap(longitude, latitude) {let that thisthat.map new BMapGL.Map("container");// 创建地图实例if (longitude null ||…

亿某通电子文档安全管理系统任意文件上传漏洞 CNVD-2023-59471

1.漏洞概述 亿某通电子文档安全管理系统是一款电子文档安全防护软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产。亿赛通电子文档安全管理系统UploadFileFromClientServiceForClient接口处存在任意文件…

大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

2024年【建筑电工(建筑特殊工种)】报名考试及建筑电工(建筑特殊工种)新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年建筑电工(建筑特殊工种)报名考试为正在备考建筑电工(建筑特殊工种)操作证的学员准备的理论考试专题&#xff0c;每个月更新的建筑电工(建筑特殊工种)新版试题祝您顺利通过建筑电工(建筑特殊工种)考试。 1、【单…

内网离线搭建之----nginx高可用

1.系统版本 虚拟机192.168.9.184 虚拟机192.168.9.185 2.nginx以及依赖下载地址 nginx&#xff1a;nginx: download pcre&#xff1a;PCRE - Browse /pcre/8.45 at SourceForge.net zlib&#xff1a;zlib Home Site 基本都在置顶的资源里 3.检查环境安装依赖的依赖&#xf…

RS232转profinet网关扫码枪自由口与1500程序对比

RS232转profinet网关&#xff08;XD-PNR200&#xff09;自由口是一种用于将RS232串口信号转换为profinet协议的设备&#xff0c;它具有自由口的功能。本文以某自动化生产线为例进行案例研究。通过RS232转Profinet网关&#xff08;XD-PNR200&#xff09;&#xff0c;将生产线的多…

Elasticsearch 【版本7.X】之常用的语法大全

文章目录 监控相关 API 查看健康状况查看所有节点查看所有节点详细信息查看主节点查看所有索引查看所有分片 索引管理 创建索引查看索引查看索引字段类型修改索引字段删除索引别名 给索引添加别名查询某个索引下的别名给索引更换别名给索引解绑别名一个别名绑定多个索引查询ind…

xv6 文件系统(上)

〇、前言 本文将会结合 xv6 源码讨论文件系统的工作原理。 一、文件系统实现概述 xv6 文件系统可以用下面的图来表示&#xff1a; 按照分层的方式进行理解&#xff1a; 在最底层是磁盘&#xff0c;也就是一些实际保存数据的存储设备&#xff0c;正是这些设备提供了持久化存…

mysql函数()之常见字符串函数

MySQL中常见的字符串函数有以下几种&#xff1a; CONCAT()&#xff1a;将两个或多个字符串连接在一起。 用法&#xff1a;CONCAT(string1, string2, …) 效果图&#xff1a; LENGTH()&#xff1a;返回字符串的长度。 用法&#xff1a;LENGTH(string) 效果图&#xff1a; UP…

7款不容错过的前端特效源码分享(附源码及演示效果)

分享7款非常不错炫酷的前端特效源码 其中包含css动画特效、js原生特效、svg特效等 下面我会列出核心的代码 但你也可以点击预览获取查看该源码的最终展示效果及下载该源码资源 图像网格动画 纯js和css实现的多方位多样式的图像网格动画 点击预览获取 核心代码 <div class…

基于JSP+Servlet+JavaBean+JDBC+DAO的Web架构设计图书管理系统

系统实现如下的基本管理功能: (1)用户分为两类:系统管理员&#xff0c;一般用户。 (2)提供用户注册和用户登录验证功能:其中一个登录用户的信息有:登录用户名&#xff0c;登录密码。 (3)管理员可以实现对注册用户的管理(删除)&#xff0c;并实现对图书的创建、查询、修改和删…

Java算法(十二):【数据结构与算法】 十大排序 之 二分查法 二分查法实现详细流程图分析 实现源码实例

二分查找 二分查找 二分查找就是返回有序序列中&#xff0c;需要查找的元素索引&#xff0c;无则-1。 需求&#xff1a;二分查找&#xff1a;手写实现数组元素的查找&#xff0c;存在返回索引&#xff0c;无则返回 -1&#xff1b;实现思路&#xff1a;&#xff08;前提是有序…

“源味河南县 牛羊天下鲜”河南县有机农畜产品发布会在博鳌召开

2023年12月17日&#xff0c;由中共青海省黄南州河南蒙古族自治县委员会、县人民政府主办的“源味河南县 牛羊天下鲜”绿色有机农畜产品发布会在海南博鳌举办。原农业部党组成员、中国农产品市场协会会长张玉香&#xff0c;原农业部党组成员、中国奶业协会战略发展委员会名誉副主…

VueStu03-插值表达式

插值表达式是 vue 的一种模板语法。 语法&#xff1a;{{ 表达式 }} 总结&#xff1a;

材料论文阅读/中文记录:Scaling deep learning for materials discovery

Merchant A, Batzner S, Schoenholz S S, et al. Scaling deep learning for materials discovery[J]. Nature, 2023: 1-6. 文章目录 摘要引言生成和过滤概述GNoME主动学习缩放法则和泛化 发现稳定晶体通过实验匹配和 r 2 S C A N r^2SCAN r2SCAN 进行验证有趣的组合家族 扩大…

WEB渗透—PHP反序列化(四)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…