算法:强连通分量(SCC) Tarjan算法

news2025/1/12 18:45:29

强连通分量,不能再加任何一个点了,再加一个点就不是强连通了

 

vector<int>e[N];
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],siz[N],cnt;
void tarjan(int x){
    //入x时,盖戳,入栈
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto y:e[x]){
        if(!dfn[y]){//若y尚未访问
            tarjan(y);
            low[x]=min(low[x],low[y]);//回x时更新low
        }
        else if(instk[y]){//若y已被访问且在栈中
            low[x]=min(low[x],dfn[y]);//更新low
        }
    }
    //离x时,记录SCC
    if(dfn[x]==low[x]){//若x是SCC的根
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;//SCC编号
            siz[cnt]++;//SCC大小
        }while(y!=x);
    }
}

[USACO06JAN] The Cow Prom S - 洛谷

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int siz[N],cnt;
stack<int>q;
vector<vector<int>>e(N);
int n,m;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            siz[cnt]++;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    while(m--){
        int a,b;
        cin>>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    int ans=0;
    for(int i=1;i<=cnt;i++){
        if(siz[i]>1) ans++;
    }
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

 Trajan SCC缩点

 

我们加边的时候让出度为0的点指向入度为0的点,那么只要max(din,dout)即可

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],cnt;
int din[N],dout[N];//SCC的入度,出度
int n;
vector<vector<int>>e(N);
stack<int>q;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
        }while(y!=x);
    }
}
void solve() {
    cin>>n;
    for(int i=1,a;i<=n;i++){
        cin>>a;
        while(a){
            e[i].push_back(a);
            cin>>a;
        }
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);//一些点可能走不到,即图不连通
    }
    for(int x=1;x<=n;x++){//枚举n个点
        for(int y:e[x]){//枚举点x的邻边,x指向y
            if(scc[x]!=scc[y]){//如果x和y所在的强连通分量不一样,即不在同一个强连通分量之内
                din[scc[y]]++;//y所在的强连通分量的入度++
                dout[scc[x]]++;//x所在的强连通分量的出度++
            }
        }
    }
    int a=0,b=0;
    for(int i=1;i<=cnt;i++){
        if(!din[i]) a++;//a表示缩点后入度为0的个数
        if(!dout[i]) b++;//b表示缩点后出度为0的个数
    }
    cout<<a<<endl;
    if(cnt==1) cout<<0<<endl;//特判,如果只有一个强连通分量,那么就不用加边了
    else cout<<max(a,b)<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

[USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷 

 AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],siz[N];
int cnt;
int dout[N];
vector<vector<int>>e(N);
stack<int>q;
int n,m;
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
            siz[cnt]++;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for(int x=1;x<=n;x++){
        for(auto y:e[x]){
            if(scc[x]!=scc[y]){
                dout[scc[x]]++;
            }
        }
    }
    int sum=0;
    int cnt1=0;
    for(int i=1;i<=cnt;i++){
        if(dout[i]==0){
            sum=siz[i];
            cnt1++;
        }
    }
    if(cnt1>1) sum=0;
    cout<<sum<<endl; 
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

【模板】缩点 - 洛谷

先缩点转化成一个无环图

 

团号逆序是拓扑序,因为我们给强连通分量标号的时候是从1开始标的,于是团号小的在拓扑序的末端,这样从大到小枚举团号即为拓扑序,保证是线性的,这样dp的话才能满足无后效性

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int dfn[N],low[N],tot;
bool instk[N];
int scc[N],cnt;
int w[N],nw[N];
int n,m;
vector<vector<int>>e(N),ne(N);
stack<int>q;
int dp[N];
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    q.push(x);
    instk[x]=true;
    for(auto v:e[x]){
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(instk[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x]){
        int y;
        cnt++;
        do{
            y=q.top();
            q.pop();
            instk[y]=false;
            scc[y]=cnt;
        }while(y!=x);
    }
}
void solve() {
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        e[a].push_back(b);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]) tarjan(i);
    }
    for(int x=1;x<=n;x++){
        nw[scc[x]]+=w[x];//新点的权值
        for(int y:e[x]){
            if(scc[x]!=scc[y]){
                ne[scc[x]].push_back(scc[y]);
            }
        }
    }//缩点后建拓扑图
    for(int x=cnt;x>=1;x--){
        if(dp[x]==0){//若x为路的起点
            dp[x]=nw[x];
        }
        for(auto y:ne[x]){
            dp[y]=max(dp[y],dp[x]+nw[y]);
        }
    }
    int ans=0;
    for(int i=1;i<=cnt;i++) ans=max(ans,dp[i]);//可能图不连通,有多个强连通分量
    cout<<ans<<endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t=1;
//    cin>>t;
    while(t--) {
        solve();
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql8.0.31 源码阅读

知识背景 问&#xff1a;说说构造函数有哪几种&#xff1f;分别有什么用&#xff1f; C中的构造函数可以分为4类&#xff1a;默认构造函数、初始化构造函数、拷贝构造函数、移动构造函数。 1. 默认构造函数和初始化构造函数&#xff08;在定义类的对象时&#xff0c;完成对象…

Redis-持久化机制

持久化机制介绍 RDBAOFRDB和AOF对比 RDB rdb的话是利用了写时复制技术&#xff0c;他是看时间间隔内key值的变化量&#xff0c;就比如20秒内如果有5个key改变过的话他就会创建一个fork子进程&#xff08;bgsave&#xff09;&#xff0c;通过这个子进程&#xff0c;将数据快照进…

QT商业播放器

QT商业播放器 总体架构图 架构优点&#xff1a;解耦&#xff0c;采用生产者消费者设计模式&#xff0c;各个线程各司其职&#xff0c;通过消息队列高效协作 这个项目是一个基于ijkplayer和ffplayer.c的QT商业播放器, 项目有5部分构成&#xff1a; 前端QT用户界面 后端是集成了…

视频二维码的制作方法,支持内容修改编辑

现在学生经常会需要使用音视频二维码&#xff0c;比如外出打开、才艺展示、课文背诵等等。那么如何制作一个可以长期使用的二维码呢&#xff1f;下面来给大家分享一个二维码制作&#xff08;免费在线二维码生成器-二维码在线制作-音视频二维码在线生成工具-机智熊二维码&#x…

快速了解Spring Cache

SpringCache是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单的加一个注解&#xff0c;就可以实现缓存功能。 SpringCache提供了一层抽象&#xff0c;底层可以切换不同的缓存实现。例如&#xff1a; EHChche Redis Caffeine 常用注解&#xff1a; Enabl…

JMETER自适应高分辨率的显示器

系列文章目录 历史文章 每天15分钟JMeter入门篇&#xff08;一&#xff09;&#xff1a;Hello JMeter 每天15分钟JMeter入门篇&#xff08;二&#xff09;&#xff1a;使用JMeter实现并发测试 每天15分钟JMeter入门篇&#xff08;三&#xff09;&#xff1a;认识JMeter的逻辑控…

UG\NX二次开发 获取所有子部件,封装两个函数

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 凉夜ronin 订阅本专栏,非常感谢。 简介 UG\NX二次开发 获取所有子部件,封装两个函数 效果 获取非抑制的所有子部件 //获取非抑制的所有子部件 vector<tag_t> GetChildPart(tag_t partOcc) {…

MyBatisPlus(十)判空查询

说明 判空查询&#xff0c;对应SQL语句中的 IS NULL语句&#xff0c;查询对应字段为 NULL 的数据。 isNull /*** 查询用户列表&#xff0c; 查询条件&#xff1a;电子邮箱为 null 。*/Testvoid isNull() {LambdaQueryWrapper<User> wrapper new LambdaQueryWrapper<…

基于安卓android微信小程序的远景民宿预订小程序

运行环境 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Ma…

W25Q128芯片手册精读

文章目录 前言1. 概述2. 特性3. 封装类型和引脚配置3.1 8焊盘WSON 8x6 mm3.2其他封装 4. 引脚描述4.1 片选4.2 串行数据输入输出4.3 写保护4.4 保持脚4.5 时钟 5. 块图6. 功能描述6.1 SPI功能6.1.1 标准SPI6.1.2 双通道SPI6.1.3 四通道SPI6.1.4 保持功能 6.2 写保护6.2.1 写保护…

【Golang】gin框架入门

文章目录 gin框架入门认识gingo流行的web框架gin介绍快速入门 路由RESTful API规范请求方法URI处理函数分组路由 请求参数GET请求参数POST请求参数路径参数文件参数 响应字符串方式JSON方式XML方式文件格式设置HTTP响应头重定向YAML方式 模板渲染基本使用多个模板渲染自定义模板…

SpringCache--缓存框架 ----苍穹外卖day7

目录 简介 ​快速入门 引入依赖 常用注解​ 使用步骤 1.开启缓存注解 2. Cacheable注解 简介 快速入门 引入依赖 常用注解 使用步骤 1.开启缓存注解 2. Cacheable注解 该注解仅用于查询操作&#xff0c…

零基础部署nginx mysql springboot

参考&#xff1a;写给开发人员看的Docker干货&#xff0c;零基础部署nginx mysql springboot 一、连接linux 阿里云 参考&#xff1a;部署到Linux 可能需要购买&#xff1a;购买链接 二、安装docker # 先切换到root用户下 sudo su# 更新apt-get&#xff0c;保证apt-get最新…

Shiro应用到Web Application

一、权限基础 a) 认证(你是谁&#xff1f;) 判断你(被认证者)是谁的过程。通常被认证者提供用户名和密码。 常见的认证包含如下几种&#xff1a; 匿名认证&#xff1a;允许访问资源&#xff0c;不做任何类型的安全检查。表单认证&#xff1a;访问资源之前&#xff0c;需要提…

SpringBoot vue云办公系统

SpringBoot vue云办公系统 系统功能 云办公系统 登录 员工资料管理: 搜索员工 添加编辑删除员工 导入导出excel 薪资管理: 工资账套管理 添加编辑删除工资账套 员工账套设置 系统管理: 基础信息设置 部门管理 职位管理 职称管理 权限组管理 操作员管理 开发环境和技术 开发语…

JavaEE-线程进阶

模拟实现一个定时器 运行结果如下&#xff1a; 上述模拟定时器的全部代码&#xff1a; import java.util.PriorityQueue;//创建一个类&#xff0c;用来描述定时器中的一个任务 class MyTimerTask implements Comparable<MyTimerTask> {//任务执行时间private long …

数据分析视角中的商业分析学习笔记

数据分析一大堆&#xff0c;结果却是大家早就知道的结论&#xff1f;是工具和方法出问题了吗&#xff1f;真正原因可能是你的思维有误区。 为什么分析的这么辛苦&#xff0c;得出的结论大家早知道&#xff0c;谁谁都不满意&#xff1f;核心原因有3个&#xff1a; 分析之前&am…

DHCPsnooping 配置实验(2)

DHCP报文泛洪攻击 限制接收到报文的速率 vlan 视图或者接口视图 dhcp request/ dhcp-rate dhcp snooping check dhcp-request enable dhcp snooping alarm dhcp-request enable dhcp snooping alarm dhcp-request threshold 1 超过则丢弃报文 查看[Huawei]dis dhcp statistic…

使用python-opencv检测图片中的人像

最简单的方法进行图片中的人像检测 使用python-opencv配合yolov3模型进行图片中的人像检测 1、安装python-opencv、numpy pip install opencv-python pip install numpy 2、下载yolo模型文件和配置文件&#xff1a; 下载地址&#xff1a; https://download.csdn.net/down…

如何使用 AI与人工智能的定义、研究价值、发展阶段

目录 一、什么是人工智能 二、人工智能的研究价值 三、人工智能的发展阶段 一、什么是人工智能 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机能够模拟和执行人类智能活动的科学与技术。人工智能旨在开发智能代理&…