P3916 图的遍历 题解
一、暴力法建图+DFS遍历
上来就傻啦吧唧的暴力存图,暴力,信奉大力出奇迹!此代码过了2个测试点,其它,原因:测试数据有环!不加数组,就死循环了,表现就是!
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n; //n个结点
int m; //m条边
int res[N];
int u, v;
vector<int> p[N]; //vector存图
/**
* 深度优先搜索
* @param x 从哪个号结点出发
* @param d 已经到达过的最大号结点
*/
void dfs(int x, int ne) {
//没走过,更新最大号为d
res[x] = max(ne, res[x]);
//遍历所有出边,尝试找到更大号的结点
for (int i = 0; i < p[ne].size(); i++) dfs(x, p[ne][i]);
}
int main() {
//读入
scanf("%d%d", &n, &m);
//构建图
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
p[u].push_back(v); //正向建边,结点u出发有一条到结点v的边
}
//逐个深度优先搜索,找出每个结点能够到达的最大号结点
for (int i = 1; i <= n; i++) dfs(i, i);
//输出
for (int i = 1; i <= n; i++) printf("%d ", res[i]);
printf("\n");
return 0;
}
二、加上st数组,防止有环
此代码过了个测试点,个测试点!为啥会呢?看一下数据范围:,遍历每个结点是, 假设第一个结点就有条边,那么它需要执行次
所有节点遍历一遍历就是次,时间复杂度就是啊,一秒肯定过不了啊!
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n; //n个结点
int m; //m条边
int res[N];
int u, v;
vector<int> p[N]; //vector存图
bool st[N];
/**
* 深度优先搜索
* @param x 从哪个号结点出发
* @param ne 到达了哪个结点
*/
void dfs(int x, int ne) {
if (!st[ne]) {
st[ne] = true;
//没走过,更新最大号为d
res[x] = max(ne, res[x]);
//遍历所有出边,尝试找到更大号的结点
for (int i = 0; i < p[ne].size(); i++) dfs(x, p[ne][i]);
}
}
int main() {
//读入
scanf("%d%d", &n, &m);
//构建图
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
p[u].push_back(v); //正向建边,结点u出发有一条到结点v的边
}
//逐个深度优先搜索,找出每个结点能够到达的最大号结点
for (int i = 1; i <= n; i++) {
memset(st, false, sizeof st);
dfs(i, i);
}
//输出
for (int i = 1; i <= n; i++) printf("%d ", res[i]);
printf("\n");
return 0;
}
三、逆向思维,反向建图,AC
基本思想:
反向建边是逆向思维
,这样时可以通过打标记来更便捷地记录某点所能到达的最大点,而正向建边时可能会重复搜索,遍历整个图,会增大复杂度
逆向建图时,从大的点到小的点循环,可以保证小的点被大的点覆盖掉
,就不必再参加运算了,因为,再算也不会更大了。这样,就不必每次清空数组,很容易就跑出来了。
感悟:正向建边时,考虑一下反向建边
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n; //n个结点
int m; //m条边
int res[N];
int u, v;
vector<int> p[N]; //vector存图
/**
* 功能:填充每个结点能够到达的最大结点号
* @param x 哪个结点
* @param d 最大结点号
*/
void dfs(int x, int d) {
if (res[x]) return; //访问过
res[x] = d;
for (int i = 0; i < p[x].size(); i++) dfs(p[x][i], d);
}
int main() {
//读入
scanf("%d%d", &n, &m);
//构建图
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u, &v);
p[v].push_back(u); //反向建边
}
//从大到小,逐个深度优先搜索
for (int i = n; i; i--) dfs(i, i);
//输出每个结点的最大到达结点号
for (int i = 1; i <= n; i++) printf("%d ", res[i]);
printf("\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2018-08-09 Python中包(package)的调用方式
2018-08-09 NotePad++ 配置Python工作环境
2017-08-09 高速备份还原MYSQL数据库
2013-08-09 删除重复记录(Mysql,SqlServer,Sqlite)