[NOIP2013 普及组] 车站分级
题目描述
一条单向的铁路线上,依次有编号为 $1, 2, …, n $的 $n $个火车站。每个火车站都有一个级别,最低为 1 1 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 x x x,则始发站、终点站之间所有级别大于等于火车站$ x$ 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)
例如,下表是$ 5 趟车次的运行情况。其中,前 趟车次的运行情况。其中,前 趟车次的运行情况。其中,前 4$ 趟车次均满足要求,而第 5 5 5 趟车次由于停靠了 3 3 3 号火车站( 2 2 2 级)却未停靠途经的 6 6 6 号火车站(亦为 2 2 2 级)而不满足要求。
现有 m m m 趟车次的运行情况(全部满足要求),试推算这$ 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 ( 2 ≤ s i ≤ n ) s_i(2 ≤ s_i ≤ n) si(2≤si≤n),表示第$ i$ 趟车次有 s i s_i si 个停靠站;接下来有$ s_i$个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。
输出格式
一个正整数,即 n n n 个火车站最少划分的级别数。
样例 #1
样例输入 #1
9 2
4 1 3 5 6
3 3 5 6
样例输出 #1
2
样例 #2
样例输入 #2
9 3
4 1 3 5 6
3 3 5 6
3 1 5 9
样例输出 #2
3
提示
对于$ 20%$的数据, 1 ≤ n , m ≤ 10 1 ≤ n, m ≤ 10 1≤n,m≤10;
对于 50 % 50\% 50%的数据, 1 ≤ n , m ≤ 100 1 ≤ n, m ≤ 100 1≤n,m≤100;
对于 100 % 100\% 100%的数据, 1 ≤ n , m ≤ 1000 1 ≤ n, m ≤ 1000 1≤n,m≤1000。
分析
- 只要是涉及到级别、优先级,要干什么,就必须先干什么的这类型的题,我们就要考虑到拓扑排序;此题状态转移方程和1352:【例4-13】奖金——拓扑排序一样,并且同样也是拓扑排序;
- 火车没有停的车站,一定比上面的停过的车站等级小,所以 没经过的停车站 指向 经过的停车站;所以vis标记下经过的停车站,存进stop,然后后续建边用;
- f[i]的含义就是第i个车站的等级,首先f初始化为1,表示最少都是一个级别,状态转移方程和奖金一样,f[v] = max(f[v], f[u] + 1);前面的结点指向后面结点,那说明就不在一层,尽量少分为几个不同的级别,所以+1,前后相差一个级别即可(奖金那个尽可能少发钱,那他的上司比下一级多1元即可,和这个题的状态转移方程一样);
- 为了防止重边,用一个visEdge去标记已经建立过的边,RE的都是因为没有处理重边造成的,一开始多组输入,我把标记重边的visEdge也初始化了,这不等于没有处理重边,一直RE…,太难受了;
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 1010 * 1010 / 2;
int n, m, ans;
int f[N];
int h[N], e[M], ne[M], idx;//邻接表
int d[N];//入度
int q[N];//队列
int vis[N];//标记车站是否停靠了
int visEdge[N][N];//标记边是否已经建了
int stop[N];//存放每个车次停靠的车站
void add(int a, int b) {
e[++idx] = b, ne[idx] = h[a], h[a] = idx;
}
void topSort() {
int hh = 0, tt = -1;
//度0入队
for (int i = 1; i <= n; ++i) {
if (!d[i])
q[++tt] = i;
}
while (hh <= tt) {
//队头出队
int u = q[hh++];
//遍历u指向的边
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
//删除u->v这条边
d[v]--;
//前后差一级即可
f[v] = max(f[v], f[u] + 1);
if (!d[v])
q[++tt] = v;
}
}
}
int main() {
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
memset(vis, 0, sizeof vis);
int cnt;
cin >> cnt;
for (int j = 1; j <= cnt; ++j) {
cin >> stop[j];
//将火车停靠的车站标记1
vis[stop[j]] = 1;
}
//没有停的车站,一定比上面经过停车站的车站等级小,所以 没经过的停车站 指向 经过的停车站
for (int j = stop[1]; j <= stop[cnt]; ++j) {
if (!vis[j]) { //表示j是没有停的
for (int k = 1; k <= cnt; ++k) { //经过的停车站
if (!visEdge[j][stop[k]]) {//避免重边
visEdge[j][stop[k]] = 1;
add(j, stop[k]);
d[stop[k]]++;//入度++
}
}
}
}
}
//初始化f
for (int i = 1; i <= n; ++i) {
f[i] = 1;
}
topSort();
for (int i = 1; i <= n; ++i) {
ans = max(f[i], ans);
}
cout << ans;
return 0;
}