P1254 導彈攔截

~hsm~發表於2019-01-21

題目

https://www.luogu.org/problemnew/show/P1020

描述 Description
某國為了防禦敵國的導彈襲擊,發展出一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於前一發的高度。某天,雷達捕捉到敵國的導彈來襲,由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈。
  輸入導彈的枚數和導彈依次飛來的高度(雷達給出的高度資料是不大於30000的正整數,每個資料之間有一個空格),計算這套系統最多能攔截多少導彈?如果要攔截所有導彈最少要配備多少套這種導彈攔截系統?
輸入格式 Input Format
第一行數字n表示n個導彈(n<=200)
第二行n個數字,表示n個導彈的高度
輸出格式 Output Format
一個整數,表示最多能攔截的導彈數
一個整數,表示要攔截所有導彈最少要配備的系統數
樣例輸入 Sample Input
8
389 207 155 300 299 170 158 65
樣例輸出 Sample Output
6
2
時間限制 Time Limitation
1s
來源 Source
noip原題

程式碼

O(n2)作法

#include<bits/stdc++.h>
using namespace std;
int v[10005],f[10005],g[10005]={0,1},maxn,minn; 
//maxn表示最長上升子序列長度,minn表示最長下降子序列的長度
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&v[i]); //輸入每枚導彈的高度
        f[i]=g[i]=1; 
        for(int j=1;j<i;j++)
        {
            if(v[i]<=v[j]) //求最長上升子序列
            	f[i]=max(f[i],f[j]+1);
            if(v[i]>v[j]) //求最長下降子序列
        		g[i]=max(g[i],g[j]+1);
        }
        if(f[i]>maxn) //求出最長上升子序列的長度
        	maxn=f[i];       
        if(g[i]>minn) //求出最長下降子序列的長度
            minn=g[i];        
    }
    printf("%d\n%d\n",maxn,minn); 
    /*輸出最長上升子序列長度與最長下降子序列的長度,也就是需要多少套攔截
    系統與一套系統最多攔截多少枚導彈*/
    return 0;
}
//本題將經典的動態規劃的模板融入進題幹裡,關鍵是要把它挖掘出來,就成功了!

O(nlogn)作法

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int tree[maxn],z[maxn],a[maxn],n,tot=1,ans;
inline int lowbit(int x)
{
    return x & -x;
}
inline void add(int x,int k)//這是用來求單調上升子序列的
{
	while (x<=n)
	{
		tree[x]=max(tree[x],k);
		x+=lowbit(x);
	}
}
inline int ask(int x)//這是用來求單調上升子序列的
{
    int ans=0;
    while (x)
    {
    	ans=max(ans,tree[x]);
    	x-=lowbit(x);
	}
	return ans;
}
inline int que(int x)//這是用來求最長單調不升子序列的
{
    int ans=0;
    while (x<=n)
    {
    	ans=max(ans,tree[x]);
    	x+=lowbit(x);
	}
    return ans;
}
inline void psh(int x,int k)//這也是用來求最長單調不升子序列的
{
    while (x)
    {
    	tree[x]=max(tree[x],k);
    	x-=lowbit(x);
	}
}
int main()
{
    while (scanf("%d",&a[tot])!=EOF)
    {
        n=max(n,a[tot]);
        z[tot]=a[tot];
        ++tot;
    }
    --tot;//讀入並統計個數
    for (int i=1;i<=tot;++i)//求最長單升子序列,樹狀陣列中儲存的是0~a[i]的最大值
    {
        int x=ask(a[i])+1;
        ans=max(ans,x);
        add(a[i]+1,x);//因為是嚴格單升所以這裡要+1
    }
    memset(tree,0,sizeof(tree));//清空樹狀陣列,用來求下面的不降子序列
    int num=0;
    for (int i=1;i<=tot;++i)//求最長不降子序列,樹狀陣列裡存的是a[i]~intree的最大值
    {
        int x=que(a[i])+1;
        num=max(num,x);
        psh(a[i],x);//因為是不升而不是嚴格單降所以不用-1或+1
    }
    printf("%d\n%d",num,ans);
    return 0;
}

相關文章