洛谷P1020 [NOIP1999 提高组] 导弹拦截 题解

Subtask #0

第一问思路:

因为发射高度一次不如一次可得知,就是求单调不上升子序列(即后一项总是不大于前一项)

可以记 d p [ i ] dp[i] dp[i] 表示「对于前 i i i 个数,在选择第 i i i 个数的情况下,得到的单调不升子序列的长度最长是多少」。于是可以分两种情况:

  • i i i 个数是子序列的第一项。则 d p [ i ] dp[i] dp[i]为1
  • i i i 个数不是子序列的第一项。选择的第 i i i 个数之前选择了第 j j j 个数。根据题意,第 j j j 个数应当小于第 i i i 个数的值 。枚举这样的 j j j ,可以得到状态转移方程:
dp[i]=max(dp[i],dp[j]+1)
  • 考虑到求最大拦截导弹的数,所以在 d p dp dp 数组中寻找最大值:
ans=max(ans,dp[i]);
//1≤i≤n

所以,第一问代码长这样:

for(int i=1;i<=n;i++)
	{
        for(int j=i;j>0;j--)if(a[i]<=a[j])dp[i]=max(dp[i],dp[j]+1);
        ans=max(ans,dp[i]);
    }
    cout<<ans-1<<endl;

时间复杂度为 O ( n 2 ) \mathcal O(n^2) O(n2)

第二问思路:

其实就是求输入进来的数组的最长不上升子序列

这里的 d p [ i ] dp[i] dp[i] 也可分为两种情况

  • i i i 个数是子序列的第一项。则 d p [ i ] dp[i] dp[i]为1
  • i i i 个数不是子序列的第一项。选择的第 i i i 个数之前选择了第 j 个数。根据题意,第 j j j 个数应当大于第 i i i 个数的值 。枚举这样的 j j j,可以得到状态转移方程:
dp[i]=max(dp[i],dp[j]+1);

所以,第二问代码长这样:

for(int i=1;i<=n;i++)dp[i]=1;
    for(int i=2;i<n;i++)
    {
        for(int j=i-1;j>=1;j--)
        {
            if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
        }
        ans=max(ans,dp[i]);
    }
    cout<<ans;

时间复杂度也为 O ( n 2 ) \mathcal O(n^2) O(n2)

参考代码:

#include<bits/stdc++.h>
using namespace std;
int n=1,ans=0,dp[100000],a[100000];
int main()
{
    while(cin>>a[n])n++;
    for(int i=1;i<=n;i++)
	{
        for(int j=i;j>0;j--)if(a[i]<=a[j])dp[i]=max(dp[i],dp[j]+1);
        ans=max(ans,dp[i]);
    }
    cout<<ans-1<<endl;
    ans=0;
    for(int i=1;i<=n;i++)dp[i]=1;
    for(int i=2;i<n;i++)
    {
        for(int j=i-1;j>=1;j--)
        {
            if(a[i]>a[j])dp[i]=max(dp[i],dp[j]+1);
        }
        ans=max(ans,dp[i]);
    }
    cout<<ans;
    return 0;
} 

Subtask #1

洛谷上的数据被加强了,要拿到200分的话,就要用 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 做法通过。

第一问思路:

因为时间复杂度要在 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 内,所以考虑二分

第二问思路:

可以考虑贪心

从左到右依次枚举每个导弹。假设现在有若干个导弹拦截系统可以拦截它,那么我们肯定选择这些系统当中位置最低的那一个。如果不存在任何一个导弹拦截系统可以拦截它,那我们只能新加一个系统了。

参考代码

#include <bits/stdc++.h>
using namespace std;
int n=1,f[1000000],a[1000000],t;
int main()
{
   while(cin>>a[n])n++;
   t=0,memset(f,0,sizeof(f)),f[0]=2147483647;
   for(int i=1;i<n;i++)
   {
   	int l=0,r=t+1,m;
   	while(r-l>1)
   	{
   		if(f[m=l+(r-l)/2]>=a[i])l=m;
   		else r=m;
   	}
   	int x=l+1;
   	if(x>t)t=x;
   	f[x]=a[i];
   }
   cout<<t<<endl;
   t=0;
   memset(f,0,sizeof(f)),f[0]=0;
   for(int i=1;i<n;i++)
   {
       int l=0,r=t+1,m;
       while(r-l>1)
       {
       	if(f[m=l+(r-l)/2]<a[i])l=m;
       	else r=m;
   	}
   	int x=l+1;
   	if(x>t)t=x;
   	f[x]=a[i];
   }
   cout<<t;
   return 0;
} 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值