You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
3.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}