做这个题的第一步是先思考应该如何建图,停靠的站之间是没有严格的大于小于关系的,所以直接连边的话是不利于等级的划分的。所以再仔细考虑一下,我们应该是在起点到终点之间, 连接停靠的车站和没有停靠的车站,因为停靠的车站等级必定是严格大于未停靠车站的等级。根据这个严格的大小关系我们可以建有向图。可以从低的指向高的,也可以从高的指向低的,结果是一样的。
这里我建图是从低等级的指向高等级。然后跑一次拓扑最长路径,最长的路径就是需要的最少的标记级别,因为最长的都可以被标记出来,那那些比较短的必然也可以被标记出来。建图要用空间换时间不然会T
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e3+5;
int n,m;
vector<int> G[maxn];//邻接表
bool vis[maxn]; //访问标记
int degree[maxn],rankk[maxn];
//度 以i为终点的最长有效路径
bool added[maxn][maxn]; //标记是否被添加
int main() {
scanf("%d%d",&n,&m);
int s,tmp;
int start,end;
memset(added,true,sizeof(added));
vector<int> unstop(maxn); //不停靠
vector<int> stop(maxn); //停靠
for(int t=0; t<m; ++t) {
memset(vis,0,sizeof(vis));
scanf("%d",&s);
start=INF,end=-1;
for(int i=0; i<s; ++i) {
scanf("%d",&tmp);
vis[tmp]=true;//停靠
start=min(start,tmp);
end=max(end,tmp);
}
unstop.clear();
stop.clear();
for(int i=start;i<=end;++i){
if(!vis[i]){
unstop.push_back(i);
}
if(vis[i]){
stop.push_back(i);
}
}
for(int i=0;i<(int)stop.size();++i){ //停靠的向未停靠的连边
for(int j=0;j<(int)unstop.size();++j){
int be=stop[i],en=unstop[j];
if(added[be][en]){ //是否已经被添加
G[be].push_back(en);
degree[en]++;
added[be][en]=false;
}
}
}
}
memset(vis,true,sizeof(vis));
queue<int> q;
for(int i=1; i<=n; ++i) {
if(degree[i]==0) {
q.push(i);
vis[i]=false;
}
}
int res=-1;
while(!q.empty()) {
int np=q.front();
q.pop();
for(int i=0; i<(int)G[np].size(); ++i) {
int to=G[np][i];
degree[to]--;
rankk[to]=max(rankk[to],rankk[np]+1);//更新有效路径
res=max(rankk[to],res); //更新最长有效路径
}
for(int i=1; i<=n; ++i) {
if(vis[i]&°ree[i]==0) {
q.push(i);
vis[i]=false;
}
}
}
cout<<res+1<<endl;//初始化为0,所以+1
return 0;
}