【動態規劃】導彈攔截

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:
虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷
达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有
的导弹。 
   
输入数据: 
第一行为一个整数 N,表示飞来的导弹个数,N<=100000 
第二行为 N 个整数,依次表示导弹飞来的高度,高度数据为不大于 30000 的正整数。 
 
输出数据: 
第一行,输出计算这套系统最多能拦截多少导弹 
第二行,输出要拦截所有导弹最少要配备多少套这种导弹拦截系统。   
 
样例 
输入文件:missile.in 
8 
389 207 155 300 299 170 158 65   
 
输出文件:missile.out 
6   
2 
這是一道線性動態規劃,但需要單調隊列和二分優化。
優化前的求最長不降子序列的方法:用f[i]表示以i結尾的最長不降子序列的長度,然後用這個方程f[i] = max(f[j]) + 1(j的高度必須大於等於i的高度)轉移。

優化方法:
求最長不降子序列時,維護一個len[height]值,即f值為height的最大下標,則每次只需要找到一個在len數組中的高度恰好大於f[i]的值,這樣就可以使用二分優化了。
由於len數組是單調的,每次用二分找到這個位置,最後形成的一個最大長度的len數組即為所求。


對於題目中的第二個問,只需要求一次最長上升子序列即可。

ACCode:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>

using std::max;
using std::min;

const char fi[] = "missile.in";
const char fo[] = "missile.out";
const int maxN = 100010;
const int MAX = 0x3fffff00;

int len[maxN];
int h[maxN];
int n;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }
    
  void readdata()
  {
    scanf("%d", &n);
    for (int i = 1; i < n + 1; ++i)
      scanf("%d", h + i);
  }
  
  void work()
  {
    int ans = 0;
    len[0] = MAX;
    for (int i = 1; i < n + 1; ++i)
    {
      int l = 0, r = n;
      int c = l, Now;
      while (l <= r)
      {
        int Mid = (l + r) >> 1;
        if (h[i] < len[Mid])
        {
          c = Mid;
          l = Mid + 1;
        }
        else r = Mid - 1;
      }
      Now = c + 1;
      len[Now] = max(len[Now], h[i]);
      ans = max(ans, Now);
    }
    printf("%d\n", ans);
    ans = 0;
    len[0] = 0;
    for (int i = 1; i < n + 1; ++i)
      len[i] = MAX;
    for (int i = 1; i < n + 1; ++i)
    {
      int l = 0, r = n;
      int c = l, Now;
      while (l <= r)
      {
        int Mid = (l + r) >> 1;
        if (h[i] >= len[Mid])
        {
          c = Mid;
          l = Mid + 1;
        }
        else r = Mid - 1;
      }
      Now = c + 1;
      len[Now] = min(len[Now], h[i]);
      ans = max(ans, Now);
    }
    printf("%d", ans);
  }
  
int main()
{
  init_file();
  readdata();
  work();
  exit(0);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值