P1020 [NOIP1999 普及组] 导弹拦截

P1020 [NOIP1999 普及组] 导弹拦截

第一问的n^2 作法(最好想到的)

arr存数据,dp[i]存第i个元素是第k个被拦截的(也就是不上升序列的第几位),通过第二重循环,循环n次查找到dp[i]max。

    for (int i = 2; i <= cnt; i++)
    {
        dp[i] = 1;//每个位置都默认为1
        for (int j = 1; j < i; j++)
        {
            if (arr[i] <= arr[j])
            {
                dp[i] = max(dp[j] + 1, dp[i]); // 动态规划求最大值
            }
        }
    }

第一问的nlogn作法(dp+二分+贪心)

怎么考虑logn?那么就需要想到查找时如何加快效率,只能用二分了,如果要用二分需要一个单调的数组,题目要求不上升序列,那么可以考虑用dp数组去存一个不上升序列的数组(怎么可以考虑?有一说一没想到)具体操作逻辑口述:外层循环从1~n,在循环到第i次的时候-----需要给arr[i]存放进dp数组中,构造成一个不上升序列,那么考虑dp[cnt](dp数组中最后一个元素,也就是从数组1~i-1最小的数) 如果dp[cnt]>=arr[i],那么dp[++cnt]=arr[i] 否则用二分找到arr[i]能排在dp数组中第几个数中。代码如下

  for (int i = 2; i <= n; i++)
    {
        // cout << cnt << endl;
        if (arr[i] <= dp[cnt])
            dp[++cnt] = arr[i];
        else
        {                                                                           // 要知道这里dp是一个递减的序列了,我要找到第一小于arr[i]的下标并且替换(这里实际上想的是贪心,用更大的数替换一个等下标的数字,同时结果保存了,为了后续迭代更新)
            int pos = upper_bound(dp + 1, dp + 1 + cnt, arr[i], greater<int>()) - dp; // 返回的是dp的下标(小于等于arr[i]的第一个元素哈,如果是相等也一样)
            dp[pos] = arr[i];
        }
    }

看题解时upper_bound和lower_bound学习

upper_bound(num,num+n,k)是返还num数组中大于k的第一个元素的地址

lower_bound(num,num+n,k)是返还num数组中第一个大于或等于k的第一个元素的地址

因此要找到相应元素,应该int pos=upper_bound(num,num+n,k)-num;(如果没找到返回结尾元素下标)其中pos储存的是下标

lower_bound(num,num+n,k,greater<int>())是返还小于等于,upper_bound同理(这里注意数组要是从大到小,不存在的话会返回下标0)

第二问的nlogn做法(dp+二分+贪心)

如何考虑所需要的系统数目?同理用dp数组储存,dp[i]储存的是第i个系统最后一次抵挡的导弹的高度,由于logn,我们需要让dp继续保持一种单调性,而这种单调性是单调递增的,if(arr[i]>dp[cnt])则dp[++cnt]=arr[i],如果arr[i]小于dp[cnt],我们用二分找到dp数组中第一个大于arr[i]的下标(也就是所需要的最低能够抵挡该导弹的系统,此处思想就是贪心)dp数组的个数就是答案

代码如下:

   for (int i = 2; i <= n; i++)
    {
        if (arr[i] > dp[cnt])
            dp[++cnt] = arr[i];
        else
        {
            int pos = lower_bound(dp + 1, dp + 1 + cnt, arr[i]) - dp;
            dp[pos] = arr[i];
        }
    }

AC代码:

#include <bits/stdc++.h>
using namespace std;
int n = 0;
const int N = 1e5 + 10;
int arr[N];
int dp[N];
int main()
{
    while (cin >> arr[++n])
    {
    }
    n--;
    // 1~n存放题给的数据
    dp[1] = arr[1];
    int cnt = 1; // cnt是末尾指针
    for (int i = 2; i <= n; i++)
    {
        if (arr[i] <= dp[cnt])
            dp[++cnt] = arr[i];
        else
        {                                                                             // 要知道这里dp是一个递减的序列了,我要找到第一小于arr[i]的下标并且替换(这里实际上想的是贪心,用更大的数替换一个等下标的数字,同时结果保存了,为了后续迭代更新)
            int pos = upper_bound(dp + 1, dp + 1 + cnt, arr[i], greater<int>()) - dp; // 返回的是dp的下标(小于等于arr[i]的第一个元素哈,如果是相等也一样)
            dp[pos] = arr[i];
        }
    }
    cout << cnt << endl;
    cnt = 1;
    memset(dp, 0, sizeof dp);
    dp[1] = arr[1];
    for (int i = 2; i <= n; i++)
    {
        if (arr[i] > dp[cnt])
            dp[++cnt] = arr[i];
        else
        {
            int pos = lower_bound(dp + 1, dp + 1 + cnt, arr[i]) - dp;
            dp[pos] = arr[i];
        }
    }
    cout << cnt << endl;
    return 0;
}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值