洛谷 P1726:上白泽慧音 ← Tarjan算法

【题目来源】
https://www.luogu.com.cn/problem/P1726

【题目描述】
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。
人间之里由
N 个村庄(编号为 1⋯N)和 M 条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用 1 和 2 来标记。如果存在由村庄 A 到达村庄 B 的通路,那么我们认为可以从村庄 A 到达村庄 B,记为 (A,B)。当 (A,B) 和  (B,A) 同时满足时,我们认为 A,B 是绝对连通的,记为 〈A,B〉。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄 X,Y 都满足 〈X,Y〉。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在 1,3,4 和 2,5,6 这两个最大连通区域时,输出的是 1,3,4。

【输入格式】
第一行共两个正整数 N,M。
第 2 行至第 M+1 行,每行有三个正整数 a,b,t。若
t=1 则表示存在从村庄 a 到 b 的单向道路,若 t=2 表示村庄 a,b 之间存在双向通行的道路。保证每条道路只出现一次。

【输出格式】
第一行输出 1 个整数,表示最大的绝对连通区域包含的村庄个数。
第二行输出若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。

【输入样例】
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1

【输出样例】
3
1 3 5

【说明/提示】
对于 60% 的数据,1≤N≤200,且 0≤M≤10^4;
对于 100% 的数据,
1≤N≤5×10^3,且 0≤M≤5×10^4

【算法分析】
● Tarjan算法简介
Tarjan算法是一个基于深搜(DFS),用于求解图中
强连通分量(SCC)割点/割边问题的算法。
Tarjan算法在搜索的过程中,会形成一棵
搜索树

由上图易知,横向边和前向边都无法构成回路,即不能形成大于一个点的强连通分量。所以,在本题中应用 Tarjan 算法的关键,就是找出重要的后向边,用于求解图中的强连通分量。

● Tarjan算法中两个核心数组
dfn[x]low[x]
dfn[x]:表示一个结点的时间戳。时间戳是在 dfs 的过程中,每个结点第一次被访问的时间顺序。
low[x]:表示一个结点的追溯值。追溯值是一个结点通过后向边能够回到的最早结点的编号,也就是 dfn 的最小值
dfn 可以通过两种方式更新:
(1)若在搜索树上 x 是 y 的父结点,那么 low[x]=min(low[x],low[y])
(2)若是
非树边(横向边、后向边),那么 low[x]=min(low[x],dfn[y])
要注意的是,
在无向图中,儿子到父亲的那条边不处理,要不然 low 值就都是 1 了,没有意义。

● Tarjan算法关于图中强连通分量的
判定条件
与当前点 cur 关联的所有边都被遍历完之后,判断它的 dfn 是否等于 low(即,dfn[cur] == low[cur])?若为 True,则表示搜到了一个强连通分量的根。之后,出栈,直到当前结点 cur 终止。出栈的都是这个强连通分量集合内的结点;若为 False,回溯。

● 本题可考虑使用
优先队列实现按字典序输出结果。
优先队列:
https://blog.csdn.net/qq_19656301/article/details/82490601

#include <bits/stdc++.h>
using namespace std;

priority_queue<int,vector<int>,less<int> > down;
priority_queue<int,vector<int>,greater<int> > up;

int main() {
    int n,x;
    cin>>n;
    while(n--) {
        cin>>x;
        down.push(x);
        up.push(x);
    }

    while(!down.empty()) {
        cout<<down.top()<<" ";
        down.pop();
    }
    cout<<endl;
    while(!up.empty()) {
        cout<<up.top()<<" ";
        up.pop();
    }
    
    return 0;
}

/*
in:
5
6 9 2 7 1

out:
9 7 6 2 1
1 2 6 7 9
*/

● 链式前向星:https://blog.csdn.net/hnjzsyjyj/article/details/139369904
val[idx]:存储编号为 idx 的边的值 
e[idx]:存储编号为 idx 的结点的值
ne[idx]:存储编号为 idx 的结点指向的结点的编号
h[a]:存储头结点 a 指向的结点的编号

【算法代码一】

#include <bits/stdc++.h>
using namespace std;

const int maxn=5e3+5;
vector<int> g[maxn];
stack<int> stk;
int scc[maxn]; //scc[i]:第i个强连通分量的结点数
int cnt_scc;
int dfn[maxn]; //dfn[i]:结点i的时间戳
int low[maxn]; //low[i]:结点i的追溯值。也就是dfn的最小值。
int vis[maxn];
int cnt[maxn];
int ts; //时间戳 timestamp

void tarjan(int u) {
    dfn[u]=low[u]=++ts;
    stk.push(u);
    vis[u]=1;
    for(int t:g[u]) {
        if(dfn[t]==0) {
            tarjan(t);
            low[u]=min(low[u],low[t]);
        } else if(vis[t]) {
            low[u]=min(low[u],low[t]);
        }
    }

    if(low[u]==dfn[u]) {
        cnt_scc++;
        while(stk.top()!=u) {
            int t=stk.top();
            vis[t]=0;
            scc[t]=cnt_scc;
            cnt[cnt_scc]++;
            stk.pop();
        }
        scc[u]=cnt_scc;
        stk.pop();
        vis[u]=0;
        cnt[cnt_scc]++;
    }
}

int main() {
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=m; i++) {
        int x,y,op;
        cin>>x>>y>>op;
        g[x].push_back(y);
        if(op==2) g[y].push_back(x);
    }

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

    int t=0, ans=-1;
    for(int i=1; i<=n; i++) {
        if(ans<cnt[scc[i]]) {
            ans=cnt[scc[i]];
            t=scc[i];
        }
    }
    cout<<ans<<endl;

    int cur=0;
    for(int i=1; i<=n; i++) {
        if(scc[i]==t) {
            if(cur++) cout<<" ";
            cout<<i;
        }
    }
    cout<<endl;

    return 0;
}


/*
in:
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1

out:
3
1 3 5
*/


【算法代码二】

#include <bits/stdc++.h>
using namespace std;

const int maxn=5e5+5;
const int inf=0x3f3f3f3f;

int dfn[maxn]; //dfn[i]:结点i的时间戳
int low[maxn]; //low[i]:结点i所能到达的最小时间戳
int lie[maxn]; //lie[i]:结点i位于的强联通分量
int siz[maxn]; //siz[i]:第i个强联通分量的结点数
int stk[maxn]; //模拟栈
int top; //栈顶
int tot; //第tot个强联通分量
int ts; //时间戳 timestamp
int ans=-inf;
int n,m;

int e[maxn<<1],ne[maxn<<1],h[maxn],idx;
void add(int a,int b) {
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void tarjan(int u) {
    dfn[u]=low[u]=++ts;
    stk[++top]=u;
    for(int i=h[u]; ~i; i=ne[i]) {
        int v=e[i];
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        } else if(!lie[v]) low[u]=min(low[u],dfn[v]);
    }

    if(low[u]==dfn[u]) {
        lie[u]=++tot;
        siz[tot]++;
        while(stk[top]!=u) {
            siz[tot]++;
            lie[stk[top]]=tot;
            top--;
        }
        top--;
    }
}

int main() {
    memset(h,-1,sizeof(h));
    cin>>n>>m;
    for(int i=1; i<=m; i++) {
        int x,y,f;
        cin>>x>>y>>f;
        if(f==1) add(x,y);
        if(f==2) add(x,y),add(y,x);
    }

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

    for(int i=1; i<=tot; i++) ans=max(ans,siz[i]);
    cout<<ans<<endl;
    for(int i=1; i<=n; i++) {
        if(siz[lie[i]]==ans) {
            int cur=lie[i];
            for(int j=i; j<=n; j++) {
                if(lie[j]==cur) cout<<j<<" ";
            }
            return 0;
        }
    }

    return 0;
}

/*
in:
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1

out:
3
1 3 5
*/




【参考文献】
https://www.cnblogs.com/dgsvygd/p/16579748.html
https://www.cnblogs.com/bloodstalk/p/17432793.html
https://blog.csdn.net/yikeyoucaihua/article/details/135832822
https://www.luogu.com.cn/problem/solution/P1726
https://www.luogu.com.cn/article/6dgk1yk1
https://blog.csdn.net/qq_30277239/article/details/118683637
https://zhuanlan.zhihu.com/p/161537289




 

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

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

相关文章

【Java】已解决java.util.concurrent.RejectedExecutionException异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.util.concurrent.RejectedExecutionException异常 一、问题背景 java.util.concurrent.RejectedExecutionException是Java并发编程中常见的一个异常&#xff0c;它通常发生…

前字节员工自爆:我原腾讯一哥们,跳槽去小公司做小领导,就签了竞业,又从小公司离职去了对手公司,结果被发现了,小公司要他赔80万

“世界那么大&#xff0c;我想去看看”&#xff0c;这句曾经火遍网络的辞职宣言&#xff0c;说出了多少职场人心中的渴望。然而&#xff0c;当我们真的迈出跳槽那一步时&#xff0c;才发现&#xff0c;现实远比想象中残酷得多。 最近&#xff0c;一起前字节跳动员工爆料的事件…

每日AI资讯-20240616

1. AI漫画角色一致性大突破 由中山大学和联想团队联合提出AutoStudio&#xff0c;它是一个无需训练的多智能体协同框架。AutoStudio采用基于大语言模型的三个智能体来处理交互&#xff0c;并使用基于扩散模型的Drawer生成高质量图像。实验中&#xff0c;AutoStudio无论是在定量…

使用密钥对登录服务器

目录 1、使用密钥文件登录服务器 2、登录成功画面&#xff1a; 3、如若出现以下状况&#xff0c;则说明密钥文件登录失败 1、使用密钥文件登录服务器 首先需要上传pem文件 2、登录成功画面&#xff1a; 3、如若出现以下状况&#xff0c;则说明密钥文件登录失败 解决方法&…

信息论与大数据安全知识点

文章目录 第一章 绪论&#xfffc;大数据概述大数据安全与加密技术 安全存储与访问控制技术访问控制概念早期的四种访问控制模型局限性总结 大数据场景下的访问控制技术 安全检索技术密文检索基础 安全处理技术同态加密 隐私保护技术 第一章 绪论&#xfffc; 大数据概述 大数…

总结之Docker(四)——镜像修改非ROOT用户权限后生成新镜像并发布

Docker拉去目标镜像 docker pull redis:6.2.5如果出现拉去过程超时&#xff0c;或者连接失败。 添加镜像加速器&#xff0c;以阿里云为例&#xff0c;阿里云目前推广提供镜像加速器&#xff0c;需要登录。 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 生成…

闲置资源共享平台

摘 要 随着共享经济的高速发展以及人们对物品的需求方面也越来也丰富&#xff0c;而且各大高校的大学生们的购买力也越来越强&#xff0c;随之而来的问题就是身边的闲置资源也越来越多&#xff0c;但是也有许多的大学生对物品的要求方面不是很高&#xff0c;也愿意买下经济实惠…

06 PXE高效批量网络装机

1、部署PXE远程安装服务 在大规模的Linux应用环境中&#xff08;如Web集群、分布式计算等&#xff09;&#xff0c;服务器往往并不配装备光驱设备。 在这种情况下&#xff0c;传统的USB光驱、移动硬盘等安装方法显然已经难以满足需求。 那么如何为数十台服务器裸机快速安装系…

坚持刷题|反转链表

文章目录 题目思考实现1. 迭代方式实现链表翻转2. 递归方式实现链表翻转 Hello&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天继续链表&#xff1a;反转链表 题目 LCR 024. 反转链表 思考 翻转链表是一个常见的算法问题&a…

小主机折腾记录27

1.买了一个9600k&#xff0c;3根台电 4G 2666 极光A40&#xff0c;一根台电8G2666 极光A40&#xff0c;一根国惠8G2666&#xff0c;一个惠普3热管散热器 测试结果如下 1&#xff09;三根台电 4G2666 相互兼容&#xff0c;频率2667显示正常&#xff0c;显示为美光颗粒&#xff0…

YOLOv8改进 | 卷积模块 | 用坐标卷积CoordConv替换Conv

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a;《YOLOv8改进有效涨点》专栏介绍 & 专栏目录 | 目前已有40篇内容&#xff0c;内含各种Head检测头、损失函数Loss、B…

搜维尔科技邀您共赴2024第四届轨道车辆工业设计国际研讨会

会议内容 聚焦“创新、设计、突破”&#xff0c;围绕“面向生命健康、可持续发展的轨道交通系统” 为主题&#xff0c;从数字化、智能化、人性化、绿色发展等方面&#xff0c;探索轨道交通行业的设计新趋势及发展新机遇。 举办时间 2024年7月10日-12日 举办地点 星光岛-青岛融…

STM32F4 STD标准库串口接收中断+空闲中断例程

STM32F4 STD标准库串口接收中断空闲中断例程 &#x1f516;工程基于STM32F446 ✨用惯了STM32CubeMX傻瓜式配置&#xff0c;突然改用标准库写代码&#xff0c;初始化外设内容&#xff0c;总是丢三落四的。 &#x1f4d7;串口初始化配置 void uart_init(uint32_t bound) {//GPIO…

分析师:是什么导致山寨币在本轮周期表现不佳?

在加密货币领域&#xff0c;山寨币的过度分散化问题逐渐凸显&#xff0c;成为本轮周期内其表现疲软的核心因素。经过深入研究&#xff0c;我发现这种分散化对加密货币市场的整体健康造成了严重威胁。然而&#xff0c;令人遗憾的是&#xff0c;目前看来&#xff0c;我们尚未找到…

Java基础 - 练习(三)打印空心菱形

Java基础练习 打印空心菱形&#xff0c;先上代码&#xff1a; public static void diamond() {//控制行数for (int i 1; i < 4; i) {//空格的个数for (int k 1; k < 4 - i; k) {System.out.print(" ");}//控制星星个数的时候和行有关for (int j 1; j <…

【第20章】Vue实战篇之Vue Router(路由)

文章目录 前言一、使用Vue-Router1.安装2. 创建路由器实例3. 注册路由器插件4. 根组件 二、访问路由器1.理论2.使用3. 展示 三、嵌套路由(子路由)1. 准备文件2. 配置路由3. 菜单配置4. 展示 总结 前言 Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成&#xff0c;…

【机器学习】第2章 线性回归及最大熵模型

一、概念 1.回归就是用一条曲线对数据点进行拟合&#xff0c;该曲线称为最佳拟合曲线&#xff0c;这个拟合过程称为回归。 2.一个自变量 叫 一元线性回归&#xff0c;大于一个自变量 叫 多元线性回归。 &#xff08;1&#xff09;多元回归&#xff1a;两个x&#xff0c;一个…

BUU CODE REVIEW 11 代码审计之反序列化知识

打开靶场&#xff0c;得到的是一段代码。 通过分析上面代码可以构造下面代码&#xff0c;获取到序列化之后的obj。 <?php class BUU {public $correct "";public $input "";public function __destruct() {try {$this->correct base64_encode(u…

森林之下延迟高如何处理 森林之下联机卡顿的解决方法

森林之下是一款结合了农场模拟、恐怖生存的游戏&#xff0c;玩家需要管理一个被“闹鬼的树林”包围的农场&#xff0c;种植农作物&#xff0c;拯救、驯服、饲养动物&#xff0c;探索被诅咒的森林&#xff0c;并且收集物品来破除诅咒。这款游戏目前已经开放了demo&#xff0c;不…

基础模型服务商SiliconCloud,新注册用户赠送 14 元的配额(约 1 亿 token)

注册链接&#xff1a;https://cloud.siliconflow.cn?referrerclx1f2kue00005c599dx5u8dz 开源模型可以自己部署&#xff0c;对服务器的要求还是挺高&#xff0c;以及学习成本、部署过程成本都是比较高&#xff0c;硅基流动SiliconFlow提供了另一个方式&#xff0c;可以像使用…