洛谷·导弹拦截

初见安~这里是传送门:洛谷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——

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/82934594