[luogu p1983] 车站分级

传送门

车站分级

题目描述

一条单向的铁路线上,依次有编号为 \(1, 2, ..., n\)\(n\)个火车站。每个火车站都有一个级别,最低为 \(1\) 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 \(x\),则始发站、终点站之间所有级别大于等于火车站\(x\) 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

例如,下表是\(5\)趟车次的运行情况。其中,前\(4\) 趟车次均满足要求,而第 \(5\) 趟车次由于停靠了 \(3\) 号火车站(\(2\) 级)却未停靠途经的 \(6\) 号火车站(亦为 \(2\) 级)而不满足要求。

现有 \(m\) 趟车次的运行情况(全部满足要求),试推算这$ n$ 个火车站至少分为几个不同的级别。

输入输出格式

输入格式

第一行包含 \(2\) 个正整数 \(n, m\),用一个空格隔开。

\(i + 1\)\((1 ≤ i ≤ m)\)中,首先是一个正整数 \(s_i(2 ≤ s_i ≤ n)\),表示第$ i$ 趟车次有 \(s_i\) 个停靠站;接下来有$ s_i$个正整数,表示所有停靠站的编号,从小到大排列。每两个数之间用一个空格隔开。输入保证所有的车次都满足要求。

输出格式

一个正整数,即 \(n\) 个火车站最少划分的级别数。

输入输出样例

输入样例 #1

9 2
4 1 3 5 6
3 3 5 6

输出样例 #1

2

输入样例 #2

9 3
4 1 3 5 6
3 3 5 6
3 1 5 9

输出样例 #2

3

说明

对于\(20\%\)的数据,\(1 ≤ n, m ≤ 10\)

对于 \(50\%\)的数据,\(1 ≤ n, m ≤ 100\)

对于 \(100\%\)的数据,\(1 ≤ n, m ≤ 1000\)

分析

此题我们要抓住原题中的一句话:

如果这趟车次停靠了火车站 \(x\),则始发站、终点站之间所有级别大于等于火车站\(x\) 的都必须停靠。

反过来也就是说:

在一个车次中,停下来的车站一定比没停下来的车站的级别高。

现在让你求最少分为多少级,也就是说,我们用 \(1, 2, 3, 4\) 给级别编号,程序让我们求的就是最高的那个级别。

既然在一个车次中停下来的车站一定比没停下来的车站的级别高,我们就可以根据这个让级别低的向级别高的连边,最终就会形成一个DAG。对于这个DAG进行topsort,并同时进行递推,便可以得出每一个车站的等级了,最后取等级最大值即可。

上代码。

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-10-01 09:14:56 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-10-01 11:32:31
 */
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>

const int maxn = 1005;
const int maxm = 1005;

std :: vector <int> G[maxn];
int ind[maxn];
bool vis[maxn][maxn];

int n, m;
int topsort() {
    std :: queue <std :: pair <int, int> > q;
    int ans = 1;
    for (int i = 1; i <= n; ++i)
        if (ind[i] == 0)
            q.push(std :: make_pair(i, 1));

    while (!q.empty()) {
        int u = q.front().first, level = q.front().second;
        q.pop();
        for (int i = 0; i < G[u].size(); ++i) {
            int v = G[u][i];
            --ind[v];
            if (ind[v] == 0) {
                q.push(std :: make_pair(v, level + 1));
                ans = ans > level + 1 ? ans : level + 1;
            }
        }
    }

    return ans;
}

int main() {
    std :: scanf("%d %d", &n, &m);

    for (int i = 1; i <= m; ++i) {
        int s;
        std :: scanf("%d", &s);
        int stop[maxn];
        bool is_stop[maxn] = {false};
        for (int j = 1; j <= s; ++j) {
            std :: scanf("%d", &stop[j]);
            is_stop[stop[j]] = true;
        }

        for (int j = stop[1]; j <= stop[s]; ++j) {
            if (!is_stop[j]) {
                for (int k = 1; k <= s; ++k) {
                    if (!vis[j][stop[k]]) {
                        ++ind[stop[k]];
                        G[j].push_back(stop[k]);
                        vis[j][stop[k]] = true;
                    }
                }
            }
        }
    }

    std :: printf("%d\n", topsort());
    return 0;
}

评测记录

评测记录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值