1. 程式人生 > >洛谷·導彈攔截

洛谷·導彈攔截

初見安~這裡是傳送門:洛谷P1020

題目

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

輸入導彈依次飛來的高度(雷達給出的高度資料是 \le 50000≤50000的正整數),計算這套系統最多能攔截多少導彈,如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

輸入格式:

11行,若干個整數(個數 \le 100000≤100000)

輸出格式:

22行,每行一個整數,第一個數字表示這套系統最多能攔截多少導彈,第二個數字表示如果要攔截所有導彈最少要配備多少套這種導彈攔截系統。

輸入輸出樣例

輸入樣例:
389 207 155 300 299 170 158 65

輸出樣例:
6
2

題解

本題很明顯——求最長下降子序列,但又由於描述為“不高於”,所以要求的應為最長不升子序列。所以我們可以直接上套路!
然後就出現了另一個問題——配置套數怎麼辦?
其實有一個很神奇的結論——求一個序列裡面最少有多少最長不上升序列等於求這個序列裡最長上升序列的長度。至於如何得來——可以手動模擬分組去掉最長不升子序列,然後在每一組的最大值裡找找規律。(洛谷大佬的題解有解釋
看起來很簡單對不對!所以我們就直接套路吧!(如果和我思路一樣but過不了,那就請繼續往下看吧。

#include<bits/stdc++.h>
using namespace std;
int a[1001000];
int main()
{
    int t=0;
    int temp;
    int maxn=-32767;
    while(cin>>temp)//其實temp算是個擺設?
    {
        a[++t]=temp;
    }
    
    int dp[1001000];
    for(int i=1;i<=t;i++)
    {
        dp[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[i]<=a[j]) dp[i]=max(dp[i],dp[j]+1);//開始套路
            maxn=max(dp[i],maxn);
        }
    }
    
    cout<<maxn<<endl;
    maxn=-32767;
    int s=0;
    
    for(int i=1;i<=t;i++)
    {
        dp[i]=1;//為了節省空間,直接再來。
        for(int j=1;j<i;j++)
        {
            if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1);
            maxn=max(dp[i],maxn);
        }
    }
    cout<<maxn<<endl;
    return 0;
}

如果你就是這麼做並提交給了洛谷的話,恭喜你——超時一半。:)
(反正我交了過後是這個樣子:)O2優化都救不了我

呵呵
保持微笑:)
其實NOIP當年並沒有這麼難,應該是洛谷後來強化了的。如果你能對一半(如上),那麼當年的NOIP你就相當於過了。(至少洛谷裡會在題目旁邊給你打個√)
而若要優化的話——首先我們得手動找找規律什麼的。(*我也是看了大佬的題解才想到怎麼優化的……所以要總結一下 *)

每次比較時,當發現有比maxn更長的序列長度時,我們可以存一下,在j迴圈不必從頭來,因此需要一個真·maxn作為ans。
詳細解釋附程式碼如下——

#include<bits/stdc++.h>
using namespace std;
int a[1001000];
int dp[1001000];
int d[1001000];
int main()
{
	int t=0;
	int n=0; 
	int temp;
	int ans=0;
	while(cin>>temp)
	{
		a[++n]=temp;//n統計個數 [temp似乎是個擺設? 
	}
	
	for(int i=1;i<=n;i++)//最長非升子序列套路 
	{
		dp[i]=1;
		for(int j=t;j>0;j--)
		{
			if(a[i]<=a[d[j]])//d陣列存的是dp[i]長度對應的序列末的下標 
			{
				dp[i]=dp[d[j]]+1;
				break;//從後往前,天然保證遇到的是最大的 
			}
		}
		
		t=max(t,dp[i]);//記錄至目前最長的一個序列結尾下標
		d[dp[i]]=i;//簡單的維護過程
        ans=max(ans,dp[i]);//維護後dp不一定最後一個即為最長,所以要判斷一下。 
	}
	cout<<ans<<endl;
	ans=1;t=0; //初始化,繼續 
	
	for(int i=1;i<=n;i++)//最長上升子序列套路 
	{
		dp[i]=1;
		for(int j=t;j>0;j--)
		{
			if(a[i]>a[d[j]])//其實就是把上面的程式複製貼上然後改一下這裡的符號…… 
			{
				dp[i]=dp[d[j]]+1;
				break;
			}
		}
		
		t=max(t,dp[i]);
        d[dp[i]]=i;
        ans=max(ans,dp[i]); 
	}
	
	cout<<ans<<endl;
	return 0;
}

以上程式其實是在理解了某個大佬的方法後寫出來的:)
洛谷題解裡還有些大佬用二分,樹等等方法,可以去了解一下:)洛谷題解

這道讓我鑽研了2天的題,終於過了!(滿滿的成就感)

迎評:)
——End——