题解 洛谷 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 <vector>
#include <iostream>
using namespace std;
constexpr int N = 1005;
vector<int> 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;
}