Files
python/TangDou/CSP-J/AcWingTraining/T8/456_Topsort.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

91 lines
3.1 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 2010, M = 1000010;
const int INF = 0x3f3f3f3f;
int n, m;
int in[N];
int d[N];
int st[N];
// 邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
vector<int> path; // 拓扑路径
void topsort() {
queue<int> q;
/*
考虑极限情况:当每一个站点全部停靠的情况下,相当于左侧集合为空(不停靠的没有)
也就没有左侧集合向虚拟点连边这件事,虚拟点的入度为0,此时,虚拟点需要入队列
*/
for (int i = 1; i <= n + m; i++)
if (!in[i]) q.push(i);
while (q.size()) {
int u = q.front();
q.pop();
path.push_back(u);
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
in[j]--;
if (in[j] == 0) q.push(j);
}
}
}
int main() {
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
// 建图
memset(h, -1, sizeof h);
cin >> n >> m; // n个火车站,m趟车次
for (int i = 1; i <= m; i++) {
memset(st, 0, sizeof st); // 记录哪些是停靠点
int k;
cin >> k; // 有多少个依靠站
int S = n, T = 1;
for (int j = 1; j <= k; j++) {
int x; // 停靠站编号
cin >> x;
S = min(S, x); // 第一个停靠点
T = max(T, x); // 最后一个停靠点
st[x] = 1; // 记录哪些是停靠点,哪些不是停靠点
}
// 笛卡尔积式建图优化技巧
int u = n + i; // 分配超级源点,虚拟点点号。多条线路,每次一个超级源点,共多建超级源点m个
for (int j = S; j <= T; j++)
if (st[j]) // 如果j不是停靠点
add(u, j, 1), in[j]++; // 虚拟点向右侧集合中每个点连一条长度为1的边
else
add(j, u, 0), in[u]++; // 左侧集合向 虚拟点u 连一条长度为0的边
}
// 求拓扑序
topsort();
/*
Q:for (int i = 1; i <= n; i) d[i] = 1;
y总这句话为什么不能这样写呀 for (int i = 1; i <= n + m; i) d[i] = 1;
A:考虑一种边界情况,当全部站点都停靠的时候,此时虚拟节点是入度为0的节点,虚拟节点的等级d应该为0,
因为连向车站的边权已经是1,否则,如果虚拟节点的等级为1,那么连向的车站等级就会是2。
*/
// 递推距离初始化
for (int i = 1; i <= n; i++) d[i] = 1; // n个站点的等级最少是1,而虚拟节点的等级最少是0
// 拓扑路径+递推计算最长路
for (auto u : path) // 枚举拓扑路径的每一个点
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
d[j] = max(d[j], d[u] + w[i]); // u要求j必须距离自己>=w[i]
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, d[i]); // 找出最大值,就是最小等级
printf("%d\n", res);
return 0;
}