题意分析
题目要求给定 n 个车站分配"等级"(用整数表示),并满足所有给定车次的要求:
- 如果某趟车在区间 内停靠了某个等级为 的车站,则该区间内所有等级 的车站也必须停靠。
- 如果某车站被跳过,则其等级必须小于区间内被停靠车站的最低等级。
等价地说:
- 对于任意一趟车,其在 范围内实际停靠的车站(记作集合 )都具有较高或相同的等级;
- 而在该区间内被跳过的车站(记作 )的等级严格小于 中所有车站的等级。
这可以转化为一个偏序关系:
- 若车站 被跳过且车站 被停靠,则我们必须有 。
- 在偏序图中,可记作一条有向边 ,表示" 的等级小于 的等级"。
构造出所有"跳过站 停靠站"的有向边后,我们得到一个有向图(如果存在可行解,这个图就是一个有向无环图 / DAG)。要满足这些等级不等式且使用的等级数最少,就等价于在这个 DAG 中寻找满足"有向边单调递增"要求的整数标号方案,且使用的不同整数值最少。
结论
在有向无环图中,"所需的最少不同标号数"恰好等于该图的最长路径长度(也称 height 或最大链长度)。
- 如果图中存在一条长度为 的链 ,它们的等级必须是严格递增的,至少需要 个不同整数。
- 用拓扑排序 + 动态规划的方法可以计算最长路径长度。
求解步骤
- 读入数据,有 个车站、 趟车。
- 对每趟车:
- 读出停靠站集合 (按升序给出),令区间 表示从最小停靠站编号到最大停靠站编号;
- 把在此区间内但不在 中的车站记为跳过站,每个跳过站 对 中的任意车站 产生一条有向边 。
- 构造出一个最多包含 个节点的有向图(顶点对应车站)。
- 在这个图上做最长路径计算(题意保证图无环):
- 用拓扑排序(Kahn 算法)或 DFS-DP 来求最长路径。
- 记录最长路径长度作为最少划分的等级数。
C++ 参考实现
#include <bits/stdc++.h>
using namespace std;
static const int MAXN = 1000;
bitset<MAXN> adj[MAXN]; // adj[u][v] = 1 表示 u -> v (等级u < 等级v)
// 拓扑 + 最长路 DP
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
// 读入每趟车信息,构造图
for(int i=0; i<m; i++){
int s;
cin >> s;
vector<int> stops(s);
for(int j=0; j<s; j++){
cin >> stops[j];
// 转为 0-based
stops[j]--;
}
// 区间 [st..en]
int st = stops[0], en = stops[s-1];
// 把停靠站做成一个 bitset
bitset<MAXN> routeStops;
for(int stid: stops){
routeStops[stid] = 1;
}
// 对区间内的每个车站 x,如果 x 未被停靠,则对 T_i 所有停靠站 y 产生 x->y
for(int x = st; x <= en; x++){
if(!routeStops[x]){
// x 在本次车次被跳过
// 对所有 y in T_i, adj[x][y] = 1
adj[x] |= routeStops;
}
}
}
// 计算每个点的入度
vector<int> inDegree(n,0);
for(int u=0; u<n; u++){
// 遍历 bitset, 对置1的位置 v, inDegree[v]++
auto & bs = adj[u];
for(int v = bs._Find_first(); v < n; v = bs._Find_next(v)){
inDegree[v]++;
}
}
// 拓扑排序 + dp[u] 表示以 u 结尾的最长链长度
vector<int> dp(n,1);
queue<int> q;
// 入度为0的先入队
for(int v=0; v<n; v++){
if(inDegree[v] == 0){
q.push(v);
}
}
int ans = 1; // 最终最长路长度
while(!q.empty()){
int u = q.front();
q.pop();
// u -> v
auto &bs = adj[u];
for(int v = bs._Find_first(); v < n; v = bs._Find_next(v)){
// 更新 dp[v]
dp[v] = max(dp[v], dp[u] + 1);
ans = max(ans, dp[v]);
inDegree[v]--;
if(inDegree[v] == 0) {
q.push(v);
}
}
}
cout << ans << "n";
return 0;
}
说明
-
图的存储:
- 用大小为 的
std::bitset<1000>数组adj[]来存储图的邻接关系。 adj[u][v] = 1表示存在边 。
- 用大小为 的
-
最长路径求法:
- 使用拓扑排序 + 动态规划。
dp[v]表示以节点 为结尾的最长链长度。- 对每条边 ,松弛更新
dp[v] = max(dp[v], dp[u] + 1)。
-
最少等级数:
- 题目保证图无环,因此可以通过最长路径长度确定最少等级数。
- 输出最长路径长度即为答案。