注意事项:
本题的dp:“线性dp—最长上升子序列的长度”
本题的贪心(单调队列):“最长上升子序列模型—拦截导弹”
下面思路只讲如何运用这些东西来解这道题
强烈建议一定要先明白"拦截导弹"
题目:
为了对抗附近恶意国家的威胁,R国更新了他们的导弹防御系统。
一套系统的导弹拦截高度要么一直 严格单调上升 要么一直 严格单调下降。
例如,一套系统先后拦截了高度为 3 和高度为 4 的两发导弹,那么接下来该系统就只能拦截高度大于 4 的导弹。
给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。
输入格式
输入包含多组测试用例。
对于每个测试用例,第一行包含整数 n ,表示来袭导弹数量。
第二行包含 n 个不同的整数,表示每个导弹的高度。
当输入测试用例 n=0 时,表示输入终止,且该用例无需处理。
输出格式
对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。
数据范围
1≤n≤50
输入:
5
3 5 2 4 1
0
输出:
2
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 55;
int n, ans;
int w[N], up[N], down[N];
//u为当前数在w的下标,su为当前上升队列的长度,sd为当前下降队列的长度
void dfs(int u, int su, int sd) {
//这两步非常关键,su+sd>=ans是剪掉了所有比当前 ans/方案 差的枝,u==n说明找到了一种更好的可行方案,更新ans
if (su + sd >= ans) return;
if (u == n) {
ans = su + sd;
return;
}
//情况1.将当前数w[u]放入到上升子序列中
// 这一段其实就是"拦截导弹"那道题的贪心部分,这里强烈建议先去看懂那一道题的贪心思路和证明再来看这个会轻松很多。
// 复述一遍:
// 每次从第一个链开始,找到比当前值小,且是所有链尾中最大的那个,将其替换,也就是得到单调上升队列的思路。
// 如何证明?假设有两条链, g[1]=...x1, g[2]=...x2, 那么此时x1必定大于x2, 因为如果x2>x1, 那么g[1]应该=...x1x2。
int k = 0;
while (k < su && up[k] >= w[u]) k++;
int t = up[k];
up[k] = w[u];
if (k < su) dfs(u+1, su, sd);
else dfs(u+1, su+1, sd);
up[k] = t;
//情况2.将当前数w[u]放入到下降子序列中
// 这里思路和上面一样,反转一下,不多赘述
k = 0;
while (k < sd && down[k] <= w[u]) k++;
t = down[k];
down[k] = w[u];
if (k < sd) dfs(u+1, su, sd);
else dfs(u+1, su, sd+1);
down[k] = t;
}
int main()
{
//循环读入,多组数据,n不为0时
while (cin >> n, n) {
for (int i = 0; i<n; i++) cin >> w[i];
//ans初始为n,因为最坏情况就时n个单调队列,然后dfs来更新ans
ans = n;
dfs(0, 0, 0);
cout << ans;
}
return 0;
}
思路:
经过"拦截导弹"一题,不难看出这道题想要我们求出:
最少需要多少个 单调上升和单调下降队列 才能覆盖整个数列。
也确实没有什么好的办法来同时表示这两个的状态,dp肯定是不行了,
那就只能爆搜DFS了。
那也就是通过DFS来找到所有可能性,枚举每个导弹究竟是放在上升队列中,还是放在下降队列中,最后求出一个最小的最优方案数(系统数)。
DFS具体看代码吧,注释讲解的很清晰了。
PS:头一回写这么难的DFS,QAQ
声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流