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

题目链接

我的博客

前言

你知道你入度为 \(0\) 吗?
因为你要入队了。

思路

这道题要求至少分为几个不同的级别,那么首先想到的就是拓扑排序

如何建图呢?

我们知道,每趟列车运行情况中,所有没有停靠的站点级别必定比已经停靠的站点级别小。是不是有思路了?我们只需要让级别小的站点指向级别大的站点,然后跑拓扑排序即可。

具体的,让每次没有停靠的站点指向停靠的站点。下图为样例 \(1\) 的图(没有 \(7,8,9\) 三个点)

最后只需要看级别最高的是多少即可。

代码

#include<cstdio>
#include<iostream>

#include<algorithm>
#include<queue>
using namespace std;
#define int long long 
#define ___ __int128
#define INF 0x3f3f3f3f3f3f3f3f 
inline int Read(){
    int x=0,f=1;
    char c=getchar();
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-48;c=getchar();}
    return x*f;
}
inline void Write(int x){
	if(x<0) {putchar('-');x=-x;}
	if(x>9) Write(x/10);
	putchar(x%10+'0');
}
const int N=1e3+10,M=5e5+10;
int n,m,a[N],d[N],rk[N],ans;
//d[i]:i 结点入度
//rk[i]:i 车站级别
bool g[N][N],vis[N];
//g[i][j]:i 和 j 之间有没有连边,去重用的
struct node{
	int nxt,to;
}e[M];//边的数组一定不要开小
int head[N],num_Edge=0;
void add_Edge(int from,int to){
	e[++num_Edge].nxt=head[from];
	e[num_Edge].to=to;
	head[from]=num_Edge;
}
queue<int> q;
signed main(){
	n=Read();m=Read();
	while(m--){
		for(int i=1;i<=n;i++) vis[i]=0;//vis[i]:本次是否停靠
		int k=Read();
		for(int i=1;i<=k;i++){
			int x=Read();
			vis[x]=1;
			a[i]=x;
		}
		for(int i=a[1];i<=a[k];i++){
			if(!vis[i]){
				for(int j=1;j<=k;j++){
					if(g[i][a[j]]) continue;
					add_Edge(i,a[j]); 
					d[a[j]]++;
					g[i][a[j]]=1;
				}
			}
		}
	}
    //拓扑排序板子
	for(int i=1;i<=n;i++){
		if(!d[i]) {
			q.push(i); 
			rk[i]=1;
		}
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			rk[v]=max(rk[v],rk[u]+1);
			d[v]--;
			if(!d[v]) q.push(v); 
		}
	}
	for(int i=1;i<=n;i++){
		ans=max(ans,rk[i]);
	}
	printf("%lld\n",ans);
	return 0; 
}
posted on 2025-11-05 15:40  _Liuliuliuliuliu  阅读(13)  评论(0)    收藏  举报