题目
题解
题解: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] |
---|---|---|
5 | 5(对应5) | |
3 | 5 | 3(对应5,3) |
7 | 7(对应7) | 3 |
6 | 7 | 6(对应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