【NOIP 2013 普及组】车站分级

【题目】

题目描述:

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

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

【NOIP 2013 普及组】车站分级_第1张图片

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

输入格式:

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

输出格式:

输出只有一行,包含一个正整数,即 n 个火车站最少划分的级别数。

样例数据:

【样例1】

输入

9 2
4 1 3 5 6
3 3 5 6

输出

2

【样例2】

输入

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

输出

3

备注:

【数据范围】 
对于 20% 的数据,1 ≤ n,m ≤ 10; 
对于 50% 的数据,1 ≤ n,m ≤ 100; 
对于 100% 的数据,1 ≤ n,m ≤ 1000。 

 

【分析】

一开始只是想写一个暴力骗骗分,结果改了一下就 A 了?

具体做法就是对于一个起点是 l,终点是 r 的车次,如果 l 到 r 中有车站没停,那么它们的级别肯定比停过的级别要低,那么就从没停的向停过的连一条边,表示它的级别相对比较低,最后拓扑排序,并且做一个简单递推即可

有一个地方要注意,就是要考虑重边,即如果当前已经有这条边,以后就不用加了(不然边数会很多,会炸掉)

emmm……好吧我还是没看懂这个O(n^2\cdot m)的算法是怎么过的

 

【代码】

#include
#include
#include
#include
#define N 1005
#define M 1000005
using namespace std;
int n,m,t,ans=1;
int a[N],f[N],du[N];
int v[M],next[M],first[N];
bool used[N],have[N][N];
stacksta;
void add(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void link(int x,int s)
{
	int i;
	for(i=1;i<=s;++i)
	{
		if(!have[x][a[i]])
		{
			add(x,a[i]);
			du[a[i]]++;
			have[x][a[i]]=true;
		}
	}
}
void topology()
{
	int x,i;
	for(i=1;i<=n;++i)
	  if(!du[i])
	    f[i]=1,sta.push(i);
	while(!sta.empty())
	{
		x=sta.top();
		sta.pop();
		for(i=first[x];i;i=next[i])
		{
			f[v[i]]=max(f[v[i]],f[x]+1);
			ans=max(ans,f[v[i]]),--du[v[i]];
			if(!du[v[i]])  sta.push(v[i]);
		}
	}
}
int main()
{
	int s,i,j;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d",&s);
		memset(used,false,sizeof(used));
		for(j=1;j<=s;++j)
		{
			scanf("%d",&a[j]);
			used[a[j]]=true;
		}
		for(j=a[1];j<=a[s];++j)
		  if(!used[j])
		    link(j,s);
	}
	topology();
	printf("%d",ans);
	return 0;
}

你可能感兴趣的:(#,拓扑排序)