1. 程式人生 > >【NOIP 2013 普及組】車站分級

【NOIP 2013 普及組】車站分級

【題目】

題目描述:

一條單向的鐵路線上,依次有編號為 1,2,…,n 的 n 個火車站。每個火車站都有一個級別,最低為 1 級。現有若干趟車次在這條線路上行駛,每一趟都滿足如下要求:如果這趟車次停靠了火車站 x,則始發站、終點站之間所有級別大於等於火車站 x 的都必須停靠。(注意:起始站和終點站自然也算作事先已知需要停靠的站點)

例如,下表是 5 趟車次的執行情況。其中,前 4 趟車次均滿足要求,而第 5 趟車次由於停靠了 3 號火車站(2 級)卻未停靠途經的 6 號火車站(亦為 2 級)而不滿足要求。 

現有 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 中有車站沒停,那麼它們的級別肯定比停過的級別要低,那麼就從沒停的向停過的連一條邊,表示它的級別相對比較低,最後拓撲排序,並且做一個簡單遞推即可

有一個地方要注意,就是要考慮重邊,即如果當前已經有這條邊,以後就不用加了(不然邊數會很多,會炸掉)

好吧我還是看不懂這個O(n^2\cdot m)的演算法是怎麼過的

【程式碼】

#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#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];
stack<int>sta;
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;
}