题目地址:
https://www.acwing.com/problem/content/458/
一条单向的铁路线上,依次有编号为
1
,
2
,
…
,
n
1,2,…,n
1,2,…,n的
n
n
n个火车站。每个火车站都有一个级别,最低为
1
1
1级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站
x
x
x,则始发站、终点站之间所有级别大于等于火车站
x
x
x的都必须停靠。注意:起始站和终点站自然也算作事先已知需要停靠的站点,例如,下表是
5
5
5趟车次的运行情况。其中,前
4
4
4趟车次均满足要求,而第
5
5
5趟车次由于停靠了
3
3
3号火车站(
2
2
2级)却未停靠途经的
6
6
6号火车站(亦为
2
2
2级)而不满足要求。
现有
m
m
m趟车次的运行情况(全部满足要求),试推算这
n
n
n个火车站至少分为几个不同的级别。
输入格式:
第一行包含
2
2
2个正整数
n
,
m
n,m
n,m,用一个空格隔开。第
i
+
1
i+1
i+1行(
1
≤
i
≤
m
1≤i≤m
1≤i≤m)中,首先是一个正整数
s
i
s_i
si(
2
≤
s
i
≤
n
2≤s_i≤n
2≤si≤n),表示第
i
i
i趟车次有
s
i
s_i
si个停靠站;接下来有
s
i
s_i
si个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。
输出格式:
输出只有一行,包含一个正整数,即
n
n
n个火车站最少划分的级别数。
数据范围:
1
≤
n
,
m
≤
1000
1≤n,m≤1000
1≤n,m≤1000
对每个列车的停靠站点做分析,那么首尾两个站点即为始发站和终点站,中间停留的站点的级别一定都严格大于未停留的站点的级别。很显然是个差分约束问题,那么要求最小的级别数,就是要求差分约束的最长路。由于输入保证所有车次都满足要求,所以图是没有环的,此时可以不用SPFA来做,而用拓扑排序来做。但是这里,例如有个车总共经过了 k k k个站点,停靠了 i i i次,如果正常建图,需要连 i ( k − i ) i(k-i) i(k−i)条边,这样太耗费时间和空间了,可以采取另一种方法,在两组点之间设一个虚拟点,从左边每个点连到这个虚拟点一条边,权为 0 0 0,然后再从这个虚拟点连到右边每个点一条边,权为 1 1 1,这样边数就是 k k k,产生的图依然是拓扑图,依然可以求最长路。求最长路的话,要按拓扑序更新每个点的最长路径长度。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
// 要开1000平方条边,点数不光有车站数,还要加上线路数,线路数就是虚拟点数
const int N = 2010, M = 1000010;
int n, m;
int h[N], e[M], ne[M], w[M], idx;
int q[N];
// 拓扑排序统计入度
int d[N];
int dist[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
// b的入度加1
d[b]++;
}
void topsort() {
int hh = 0, tt = 0;
for (int i = 1; i <= n + m; i++)
if (!d[i])
q[tt++] = i;
while (hh < tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int v = e[i];
if (--d[v] == 0) q[tt++] = v;
}
}
}
int main() {
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 1; i <= m; i++) {
memset(st, 0, sizeof st);
int cnt;
scanf("%d", &cnt);
int start = n, end = 1;
while (cnt--) {
int stop;
scanf("%d", &stop);
start = min(start, stop);
end = max(end, stop);
st[stop] = true;
}
// 建虚拟点
int v = n + i;
for (int j = start; j <= end; j++)
if (!st[j]) add(j, v, 0);
else add(v, j, 1);
}
topsort();
for (int i = 1; i <= n; i++) dist[i] = 1;
for (int i = 0; i < n + m; i++) {
int v = q[i];
for (int j = h[v]; ~j; j = ne[j])
dist[e[j]] = max(dist[e[j]], dist[v] + w[j]);
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, dist[i]);
printf("%d\n", res);
return 0;
}
时间复杂度 O ( N + M ) O(N+M) O(N+M),空间 O ( N ) O(N) O(N), N N N和 M M M分别是图的点数和边数。