题解 洛谷 Luogu P1983 [NOIP 2013 普及组] 车站分级 拓扑排序 C++

题目

传送门

P1983 [NOIP 2013 普及组] 车站分级 - 洛谷 | 计算机科学教育新生态https://www.luogu.com.cn/problem/P1983https://www.luogu.com.cn/problem/P1983https://www.luogu.com.cn/problem/P1983

思路

大小等级划分中,要划分的级别的数目的最小值,就是 DAG 的层数,通过拓扑排序求得

建模

知道了这个题要用拓扑排序做后,最关键的就是:怎么建图?

一个车次中,停靠的站台级别大小不确定,未停靠的站台的级别小于停靠的站台

所以在未停靠的站台与停靠的站台之间建边,令未停靠的站台指向停靠的站台

如何枚举未停靠的站台与停靠的站台

可以用标记数组记录某个站台是否发生停靠

我没这么做,我是用双指针的,不过二者效率差别不大

时间效率差不多,空间效率上,双指针只比这样节省了 bool[1000] 的空间

细节

直接建边的话会有非常多重边,不去重的话基本上会 MLE,需要高效去重

邻接矩阵自身可以高效去重,但是我比较喜欢用邻接表,于是选择再开一个 bool 邻接表辅助去重

(unordered) set/map 作为邻接表的子数据结构也可去重,但是本题边特别多,会 TLE

代码

#include 
#include 
using namespace std;
constexpr int N = 1005;
vector to[N];
int from[N], n, m, s, station[N], q[N], head, tail = -1, cnt;
bool mk[N][N]; //bool 邻接矩阵辅助去重
void toposort()
{
	for (int i = 1; i <= n; ++i)
		if (from[i] == 0)
			q[++tail] = i;
	while (head <= tail && ++cnt) //cnt 是层数
		for (int i = 0, sz = tail - head + 1; i < sz; ++i) //以层为操作单位,拓扑排序模板
			for (auto X : to[q[head++]])
				if (--from[X] == 0)
					q[++tail] = X;
}
int main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	cin >> n >> m;
	while (m--)
	{
		cin >> s;
		for (int i = 0; i < s; ++i) cin >> station[i];
		for (int x = station[0] + 1, p1 = 1; p1 < s; ++x)
		{//双指针,x 为所有站台,p1 指向发生停靠的站台,且是下一个当 (x == station[p1]) 时,该跳过的站台
			if (x == station[p1]) { ++p1; continue; } //跳过这个站台,指针后移
			for (int p2 = 0; p2 < s; ++p2) //枚举所有发生停靠的站台,建边
			{
				int y = station[p2];
				if (mk[x][y]) continue; //去重
				to[x].emplace_back(y);
				++from[y]; //入度
				mk[x][y] = true;
			}
		}
	}
	toposort();
	cout << cnt; //层数就是答案
	return 0;
}

你可能感兴趣的:(c++,数据结构,算法,图论,拓扑排序)