1. 程式人生 > >【原創】【NOIP1999】攔截導彈

【原創】【NOIP1999】攔截導彈

[NOIP1999]攔截導彈

時間限制: 1 Sec  記憶體限制: 64 MB
提交: 666 解決: 233

題目描述

某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲。由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。 輸入導彈依次飛來的高度,計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

輸入

第1行:依次輸入若干個導彈的高度H(1≤H≤30000),導彈的個數N≤5000

輸出

第1行:一個整數,表示單枚炮彈能攔截多少導彈 第2行:一個整數,表示攔截所有導彈最少要配備多少套這種導彈攔截系統

樣例輸入

389 207 155 300 299 170 158 65

樣例輸出

62

提示

第2問最直觀的演算法是貪心,但是反例也容易找到,如: 6 5 1 7 3 2 如果第一次打6 5 3 2,顯然還要打兩次,而最好的方案是6 5 1/7 3 2。
這是一道很經典的題目,題裡涵蓋了對動歸,搜尋,貪心的考察。那麼,就讓我們好好地來分析分析這道題目吧。 這道題目有兩個小問。先來看看第一個小問。 我在題目中用紅色標記出了一些關鍵資訊,透過這些資訊,我們可以發現,這個炮彈是從高往低掉,看來我們要找“最長下降序列”?不不不,再看看那幾個紅得發黃
字,“不得高於”! 注意了,是“最長不上升序列”!是“≤”! 別被坑了!(我也被坑了!) 好啦,怎麼找這條序列呢? 首先,我們要找的這串數要滿足兩個條件,“最長”和“不上升”。 就是說這串數每個數都≤上一個數,而且這串數裡的數字最多。 那麼,我們只需要把每一串都找出來再比較長短就okay了! You say:啊?那有幾千幾萬種情況你找得完? Too naive! 為了保證最長,我們要讓每一串數的起始位置儘量往前,終止位置儘量往後,沒錯吧? 所以,我們只需列舉這n個數,以第i個數為這串數的起始位置,看誰串的數多,就可以了。 下一個問題,最少(發)射幾(發)(彈才能打完所有的導彈)
~v~手動滑稽中~v~ 提示已經告訴我們了,貪心。 提示也說了,不可以打最長不上升序列。 那怎麼辦呢? 別急,先來分析一下為什麼“不可以打最長不上升序列”, 這是題目給的反例:6 5 1 7 3 2 最佳方案是:6 5 1 // 7 3 2 再來一組資料:4 3 4 7 6 2 1  最佳方案:4 4 2 1 // 3 // 7 6  或者 4 3 2 1 // 4 // 7 6  或者 4 4 // 7 6 2 1 // 3 來,來找規律! 我們可以發現,每一個“//”內都是不上升序列。為了讓“//”更少,我們要保證每一條序列的長度都要儘量長 這就是“不可以打最長不上升序列”的原因。我們打掉最長的,但剩下的幾組的長度就不一定很長了。 所以,我們只需要再找序列的時候,讓它儘可能長,就可以了。 而不是找到最長的一條,打掉;再找一條,再打掉…… 講了這麼多,那就詳見程式碼吧:
#include<cstdio>
#include<algorithm>
using namespace std;
int n=1,a[6006],da=-1,db,yx,f[6006],much;
void preparation()
{
	while(scanf("%d",&a[n])!=EOF) n++;
	n--;
}
void step_one()
{
	for(int i=1;i<=n;i++)
	{
		int e=0;
		for(int j=1;j<i;j++)
			if(a[j]>=a[i] and e<f[j]) e=f[j];
		f[i]=e+1;
		if(da<f[i]) da=f[i],db=i;
	}
	printf("%d\n",da);
}
void step_two()
{
	much=a[1],yx=0;
	for(int i=1;i<=n;i++)
		if(a[i]>=0) 
		{
			much=a[i];
			for(int j=i+1;j<=n;j++)
				if(a[j]>=0 and a[j]<=much)
					much=a[j],a[j]=-66;
			yx++;
		}
	printf("%d",yx);
}
int main()
{
	preparation();
	step_one();
	step_two();
}