题解:洛谷 P1983 [NOIP 2013 普及组] 车站分级

【题目来源】

洛谷:[P1983 NOIP 2013 普及组] 车站分级 - 洛谷

【题目描述】

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

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

image

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

【输入】

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

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

【输出】

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

【输入样例】

9 2 
4 1 3 5 6 
3 3 5 6 

【输出样例】

2

【算法标签】

《洛谷 P1983 车站分级》 #拓扑排序# #普及+#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 1005;  // 定义最大节点数

int n, m;            // n-节点数,m-关系组数
int board[N][N];     // 邻接矩阵表示图
int lvl[N];          // 存储每个节点的层级
int ans;             // 存储最终结果(最大层级)
int num;             // 临时变量,存储每组关系的节点数
int B[N];            // 标记数组,用于处理每组关系
int a, b, t;         // 临时变量,用于输入处理
queue<int> qu;       // 队列用于拓扑排序

// 计算节点x的入度
int du(int x)
{
    int res = 0;
    for (int i = 1; i <= n; i++)
        res += board[i][x];  // 统计所有指向x的边
    return res;
}

// 拓扑排序算法
void topo()
{
    // 初始化:将所有入度为0的节点加入队列
    for (int i = 1; i <= n; i++)
    {
        if (du(i) == 0)
        {
            lvl[i] = 1;      // 初始层级为1
            qu.push(i);
        }
    }

    // 处理队列中的节点
    while (qu.size())
    {
        int tmp = qu.front();
        qu.pop();
        
        // 遍历所有邻接节点
        for (int i = 1; i <= n; i++)
        {
            if (board[tmp][i])  // 如果存在边tmp->i
            {
                board[tmp][i] = 0;  // 删除这条边
                if (du(i) == 0)     // 如果i的入度变为0
                {
                    lvl[i] = lvl[tmp] + 1;  // 更新层级
                    qu.push(i);
                }
            }
        }
    }
}

int main()
{
    cin >> n >> m;
    
    // 处理每组关系
    while (m--)
    {
        cin >> num;
        memset(B, 0, sizeof(B));  // 清空标记数组
        
        vector<int> st;  // 存储该组的起始节点
        vector<int> ed;  // 存储该组的结束节点
        
        // 输入该组的所有节点
        cin >> a;
        B[a] = 1;
        st.push_back(a);
        
        for (int i = 2; i < num; i++)
        {
            cin >> t;
            st.push_back(t);
            B[t] = 1;
        }
        
        cin >> b;
        B[b] = 1;
        st.push_back(b);
        
        // 找出a和b之间未被标记的节点
        for (int i = a + 1; i < b; i++)
        {
            if (B[i] == 0)
                ed.push_back(i);
        }
        
        // 建立起始节点到结束节点的边
        for (int i = 0; i < st.size(); i++)
        {
            for (int j = 0; j < ed.size(); j++)
            {
                board[st[i]][ed[j]] = 1;
            }
        }
    }
    
    // 执行拓扑排序
    topo();
    
    // 找出最大层级
    for (int i = 1; i <= n; i++)
    {
        ans = max(ans, lvl[i]);
    }
    
    cout << ans << endl;
    return 0;
}

【运行结果】

9 2 
4 1 3 5 6 
3 3 5 6
2
posted @ 2026-02-18 20:03  团爸讲算法  阅读(0)  评论(0)    收藏  举报