C++图论基础拓扑排序经典OJ题流食般投喂

注意:本编标红处是可以直接纳为己用的经验条哦~


OJ题来源:洛谷

OJ题名:摄像头

OJ题归属:图论基础【拓扑排序】

解题算法:拓扑排序

经验总结:存图的那些顶点有的时候并不是依次顺位的,我们要开好足够大的空间,用数组标记一下那些顶点,这样可以提高一点效率,同时能更好的区分(比如此题的摄像头与可监视区域,如果单单只靠入度来区分,是不够的,此时有了标记数组,那么摄像头的属性就要多出一个,就有了区分的作用)。

cpp 复制代码
#include<iostream>
#include<queue>
#include<vector>

using namespace std;

const int N = 510;

int n;
vector<int> edges[N];
int in[N];
bool st[N];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int x, y, z; cin >> x >> y;
		st[x] = true;
		while (y--)
		{
			cin >> z;
			edges[x].push_back(z);
			in[z]++;
		}
	}

	queue<int> q;
	for (int i = 1; i <= 500; i++)
	{
		if (in[i] == 0 && st[i]) q.push(i);
	}

	while (q.size())
	{
		auto x = q.front(); q.pop();

		for (auto& e : edges[x])
		{
			in[e]--;
			if (in[e] == 0 && st[e]) q.push(e);
		}
	}

	int ret = 0;
	for (int i = 1; i <= 500; i++)
	{
		// 可被监视的地点且没有摄像头的区域,他的入度也是有的,所以特判的时候,
		// 入度不是唯一标准,还要是摄像头
		if (in[i] > 0 && st[i]) ret++;
	}

	if (ret == 0) cout << "YES" << endl;
	else cout << ret << endl;

	return 0;
}

OJ题来源:洛谷

OJ题名:最大食物链计数

OJ题归属:图论基础【拓扑排序】

解题算法:路径类dp + 拓扑排序

经验总结:此题的学习加深了对于dp的底层理解,在上一章动态规划的章节中,我们常常是在二维数组中进行dp,其实那种是考虑了有效和无效数据的大杂烩,而在有向图上做路径类dp,就是基于有效数据进行的dp,那些无效数据是没有被考虑上的。

在有向图上做dp其实是有一个难点的,就是五板斧中的填表顺序,在有向图上做dp不像在二维数组那样格式固定化,但是之前学习的拓扑排序其实就是量身定制适配于图的路径dp的填表顺序,解释一下:当x从队列中拿出来,那么x的dp表的位置已经跟新完毕,那么,依靠x推演的dp表格子就可以跟着跟新了。

cpp 复制代码
#include<iostream>
#include<vector>
#include<queue>

using namespace std;

const int N = 5010, MOD = 80112002;

int n, m;
vector<int> edges[N]; // 存图
int in[N], out[N]; // 存入度、出度 ---入度用于拓扑排序,出度用于找最后那个/那几个纯消费者
int f[N]; // 表示从起点到 i 位置的方案数

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int x, y; cin >> x >> y;
		edges[x].push_back(y);
		in[y]++;
		out[x]++;
	}

	//拓扑排序 + dp
	queue<int> q;
	for (int i = 1; i <= n; i++)
	{
		if (in[i] == 0)
		{
			// dp 初始化
			f[i] = 1;
			// 扔进队列
			q.push(i);
		}
	}

	for (int i = 1; i <= n; i++)
	{
		auto x = q.front(); q.pop();
		for (auto& e : edges[x])
		{
			// x -> e && f[x]已经确定,依靠x跟新的可以跟新了
			f[e] = (f[e] + f[x]) % MOD;
			in[e]--;
			if (in[e] == 0) q.push(e);
		}
	}

	int ret = 0;
	for (int i = 1; i <= n; i++)
	{
		if (out[i] == 0)
		{
			ret = (ret + f[i]) % MOD;
		}
	}

	cout << ret << endl;

	return 0;
}

OJ题来源:洛谷

OJ题名:杂务

OJ题归属:图论基础【拓扑排序】

解题算法:动态规划 + 拓扑排序

经验总结:图上的动态规划的状态转移方程的跟新是分批次的,不像是以前动态规划章节一次性更新完一个空。在此题中,在将队头元素拿出队的时候,代表此时这个空已经把前置的那些格子中的所需最大值已经跟新完毕,此时加上该位置自己的值,该位置的完整结果就出来了,在枚举指向的孩子那,状态转移方程更新的是前置那些格子中的最大值,还没有考虑当前位置自己的值。

cpp 复制代码
#include<iostream>
#include<vector>
#include<queue>

using namespace std;

const int N = 10010;

int n;
vector<int> edges[N];
int f[N];
int in[N];
int len[N];

int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		int a, b; cin >> b >> len[b];
		while (cin >> a, a)
		{
			edges[a].push_back(b);
			in[b]++;
		}
	}

	queue<int> q;
	for (int i = 1; i <= n; i++)
	{
		if (in[i] == 0) q.push(i);
	}

	int ret = 0;
	while (q.size())
	{
		auto x = q.front(); q.pop();
		// 此时 x 从队列中拿出来,x位置之前的工作花费时间最大值已经存与f[i]中
		f[x] += len[x]; // 跟新好x位置的完整结果
		ret = max(ret, f[x]); // 拿出完整结果中的最大值就是最终结果

		for (auto& e : edges[x])
		{
			// 跟新以x为前置工作的花费时间,分批次跟新出前置工作中花费时间最大值
			f[e] = max(f[e], f[x]);

			//拓扑排序的一些操作
			in[e]--;
			if (in[e] == 0) q.push(e);
		}
	}

	cout << ret << endl;

	return 0;
}
相关推荐
芯岭技术郦1 小时前
MS32C001‑C:极致成本 32 位 MCU
c语言·开发语言·单片机
C+-C资深大佬1 小时前
C++ 数字与字符串互转
java·c++·算法
nexustech1 小时前
simplejson:Python JSON 处理的备用引擎
开发语言·python·其他·json
雷工笔记1 小时前
MES系列48-MES 系统「质量管理」完整设计与实施方案
开发语言·javascript·ecmascript
小灰灰搞电子1 小时前
C++ boost::asio 详解:网络编程领域的“瑞士军刀“
网络·c++·boost
ch.ju1 小时前
Java Programming Chapter 4——The difference between overloading and overwriting.
java·开发语言
满怀冰雪1 小时前
第12篇-二分答案法-当答案不好求时如何反向搜索
java·算法
KaMeidebaby1 小时前
卡梅德生物技术快报|兔单克隆抗体应用实战:禽源病原 IFA 检测全流程拆解
前端·人工智能·物联网·算法·百度
CC数学建模1 小时前
2026年第十六届APMCM 亚太地区大学生数学建模竞赛(中文赛项)赛题A题:自来水厂水质预测与评估完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模