图的遍历(C++,BFS)

题目描述

给出 N N N 个点, M M M 条边的有向图,对于每个点 v v v,求 A ( v ) A(v) A(v) 表示从点 v v v 出发,能到达的编号最大的点。

输入格式

1 1 1 2 2 2 个整数 N , M N,M N,M,表示点数和边数。

接下来 M M M 行,每行 2 2 2 个整数 U i , V i U_i,V_i Ui,Vi,表示边 ( U i , V i ) (U_i,V_i) (Ui,Vi)。点用 1 , 2 , … , N 1,2,\dots,N 1,2,,N 编号。

输出格式

一行 N N N 个整数 A ( 1 ) , A ( 2 ) , … , A ( N ) A(1),A(2),\dots,A(N) A(1),A(2),,A(N)

样例 #1

样例输入 #1

4 3
1 2
2 4
4 3

样例输出 #1

4 4 3 4

提示

  • 对于 60 % 60\% 60% 的数据, 1 ≤ N , M ≤ 1 0 3 1 \leq N,M \leq 10^3 1N,M103
  • 对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 1 0 5 1 \leq N,M \leq 10^5 1N,M105

解题思路:

在读完题之后,顺着题的描述很容易想到可以对每个点进行一次bfs来寻找最大值

显然,这道题不会这么简单

如果这个时候仍不能换个角度考虑的话,那么我们会想到

如果能够到达一个已经bfs过的点,那么就能到达它所能到达的所有点,直接将这些点标记并返回最大值即可

然后优化之后发现还是会TLE

那怎么办呢

这时我们可以运用逆向思维,与其考虑能够到达的最大值点,不如直接考虑最大值点能到达哪些点

那么思路一下就清晰起来了

首先是存图的操作,用vector存图,同时要注意将有向边反向

int main()
{
	int n, m, tail, head;
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> tail >> head;
		map[head].push_back(tail);//反向存储有向边
	}
	return 0;

}

然后就是从最大值点开始依次进行bfs

int main()
{
	int n, m, tail, head;
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> tail >> head;
		map[head].push_back(tail);
	}
	return 0;
	for (int i = n; i >= 1; i--)
	{
		if (!(book[i]))//注意已经到访问的节点无需再次访问
		{
			bfs(i);
		}
	}
}

以下是bfs()的实现

const int max_n = int(1e5) + 1;

vector<int> map[max_n];//存图,下标表示节点
queue<int> bfs_queue;//bfs队列
bool book[max_n] = { false };//节点标记物,下标表示节点
int max_id[max_n] = { 0 };//数据处理结果,下标表示节点

void bfs(int now)//now为最大值
{
	bfs_queue.push(now);
	book[now] = true;//标记起点
	max_id[now] = now;
	while (!(bfs_queue.empty()))
	{
		for (int i = 0; i < int(map[bfs_queue.front()].size()); i++)//访问从当前队首元素所能访问的所有节点
		{
			if(!(book[map[bfs_queue.front()][i]]))
			{
				bfs_queue.push(map[bfs_queue.front()][i]);
				book[map[bfs_queue.front()][i]] = true;//注意已经访问过的点要标记,且无需取消标记
				max_id[map[bfs_queue.front()][i]] = now;//所有能访问的节点最大值均为now
			}
		}
		bfs_queue.pop();//队首节点尝试完毕,出队
	}
}

完整的代码实现如下

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

const int max_n = int(1e5) + 1;

vector<int> map[max_n];
queue<int> bfs_queue;
bool book[max_n] = { false };
int max_id[max_n] = { 0 };

void bfs(int now)
{
	bfs_queue.push(now);
	book[now] = true;
	max_id[now] = now;
	while (!(bfs_queue.empty()))
	{
		for (int i = 0; i < int(map[bfs_queue.front()].size()); i++)
		{
			if(!(book[map[bfs_queue.front()][i]]))
			{
				bfs_queue.push(map[bfs_queue.front()][i]);
				book[map[bfs_queue.front()][i]] = true;
				max_id[map[bfs_queue.front()][i]] = now;
			}
		}
		bfs_queue.pop();
	}
}

int main()
{
	int n, m, tail, head;
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> tail >> head;
		map[head].push_back(tail);
	}
	for (int i = n; i >= 1; i--)
	{
		if (!(book[i]))
		{
			bfs(i);
		}
	}
	for (int i = 1; i < n; i++)
	{
		cout << max_id[i] << ' ';
	}
	cout << max_id[n];
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值