【拦截导弹】

题目

在这里插入图片描述



​​ 题解

题解:1010. 拦截导弹(dp与贪心) - AcWing

我谈几点:

第一,由此复习了upper_bound和lower_bound函数

第二,由此学习了贪心方式求“最多分割不严格递减子序列的数目”和“最长不严格递减子序列的长度”

第三,了解到前者和“最长严格递增子序列的长度”问题的一致性



细节

int main()
{
    //cnt表示导弹系统数,len表示一个系统最多能拦截的导弹数
    int len = 0, cnt = 0;
    int a;
    while(cin >> a)
    {
        //pos1表示以a结尾的最长不升子序列长度-1
        int pos1 = upper_bound(f, f+len, a, greater<int>()) - f;
        if(pos1 == len) f[len ++] = a;
        else f[pos1] = a;

        int pos2 = lower_bound(g, g+cnt, a[i]) - g;
        if(pos2 == cnt) g[cnt ++] = a;
        else g[pos2] = a;
    }
    cout << len << endl;
    cout << cnt << endl;
    return 0;
}

第一个贪心:求最长不严格递减子序列长度

策略:如果 a 是最小,增加长度,放置于后;否则,与第一个较小值交换(因此 f f f 是 递减的)
f [ p o s 1 ] = a f[pos1] = a f[pos1]=a 对应长度为 p o s 1 + 1 pos1+1 pos1+1 的,以 a a a 结尾的最长不严格单调递减子序列,容易知道 f f f 是 递减的,因此传入 g r e a t e r < i n t > ( ) greater<int>() greater<int>(),同时为了得到第一个小于 a a a 的值的地址,采用 u p p e r    b o u n d upper\;bound upperbound

理解f[i]为什么被更新,明明按顺序不应该再被更新的啊: f [ i ] f[i] f[i] 要始终对应最优的序列,保持在一个最优的状态,也就是尽可能地让它的值最大,这样才能允许尽可能大的 a a a 值参与序列的组成。实际上它的更新,意味着原本的序列的一些值去掉了,来了一些新的值。

示例: 5 3 7 6

a值进入考虑f[0]f[1]
55(对应5)
353(对应5,3)
77(对应7)3(仍然对应5,3)
676(对应7,6)(比5,3序列状态优)

所以,不同的f之间没有必然联系。
每次都是把第一个较小值更新,就可以保证用以更新的值 a a a 一定只会比对应序列的最后一个值大(前面的值可以借过来组成新序列)

第二个贪心:求最少分割不严格递减子序列的数目

策略: 遍历各个分割序列,如果最尾端(当前值)大于等于 a a a ,则放置于后(替换值);否则开新序列(因此 g g g 递增)。
g [ i ] g[i] g[i] 对应第 i + 1 i+1 i+1 个分割序列,表示分割序列中最小的(最后的值)。容易知道 g g g 是 递增的,因此不传入 g r e a t e r < i n t > ( ) greater<int>() greater<int>(),同时为了得到第一个(最小的)大于等于 a a a 的值的地址,采用 l o w e r    b o u n d lower\;bound lowerbound

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值