知识点:深搜,迭代加深,贪心
这个题进阶指南上面给的提示是迭代加深,个人感觉这个不是迭代加深,顶多算是迭代DFS,因为迭代加深是限制搜索的深度的,然而这个题我们求最小的导弹防御系统的个数,搜索的对象选取的是每个导弹,也就是说对于每个备选答案,我们都要判断是不是能搜索完所有的导弹,我们这里迭代的对象,既是答案,也会成为剪枝的条件,如果已经用完规定数目的导弹系统,那么就不往下搜索了,但是这样一想。。。确实也限制了搜索的深度,比如导弹系统加一,那么搜索的时候深度应该是更深的,但是深多少,也就是能多填几个数(多防御几个导弹)是不确定的,不像一般的迭代加深是每次搜索深度是严格加一的,这个迭代加深只是间接的影响,不确定每次都深度多多少,
讨论完了迭代加深,然后考虑一个贪心,我们搜索的对象是导弹,每个导弹怎么处理,也就是放入哪个单调增加或者单调减少的序列里面,一开始写的时候超时了,然后忽然想到一个贪心,也就是我们不要暴力到把当前的导弹放到每个都能放到的序列里面,试想,我们要把他放到单调递增的队列里面,我们只需要放到单调增加的,可放的(这个序列里面的上一个数小于当前),并且序列末尾的数最大(也就是最接近但钱导弹)的序列里面即可,如果找不到,那么在满足当前搜索序列个数限制的条件下在增加一个单调增加的序列,也就是放进去当第一个数,这样当前导弹放单调增加序列的情况就考虑完了,放单调递减序列同理,上面贪心的思路是显然的,因为后面可能还有导弹是没有用的,我们首先是要放所有的导弹,所以要为当前的导弹选择一个,我们选择能放的最接近的,这样相较于其他可放的选择,不会使结果变得更差,
最后就是一个我自己写的时候没有考虑到的一个点,如果能找到一个单调增加的序列,那么就放进去,这之后切不可在新开一个单调增加序列,因为你就算新开一个,以后放进去也要比当前的数大,而放入一个已经有的序列里面,以后往这个序列里面放还是要比当前的数大,所以不能放已有的,再开新的序列,能放已有的序列,那么新开一个的话这搜索的情况会变得更坏,肯定不会变好
最后是程序的实现,应该是和ACWing的不同的,但是也懒得看了,多开了两个数组,一个存当前序列的最后一个数,一个存当前序列是单调增加还是减的,
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int n, a[N], b[N], c[N], max_d, flag;
void dfs(int dep, int cur) {
if (flag) return;
if (dep == n + 1) {
printf("%d\n", max_d);
flag = 1;
return;
}
int Min = N, Max = -1, p1, p2;
for (int i = 1; i <= cur; i++) {
if (a[dep] < b[i] && !c[i]) {
if (b[i] < Min) {
Min = b[i];
p1 = i;
}
}
if (a[dep] > b[i] && c[i]) {
if (b[i] > Max) {
Max = b[i];
p2 = i;
}
}
}
if (Min != N) {
int rec = b[p1];
b[p1] = a[dep];
dfs(dep + 1, cur);
b[p1] = rec;
} else if (cur < max_d) {
cur++;
b[cur] = a[dep];
c[cur] = 0;
dfs(dep + 1, cur);
cur--;
}
if (Max != -1) {
int rec = b[p2];
b[p2] = a[dep];
dfs(dep + 1, cur);
b[p2] = rec;
} else if (cur < max_d) {
cur++;
b[cur] = a[dep];
c[cur] = 1;
dfs(dep + 1, cur);
cur--;
}
}
int main() {
while (scanf("%d", &n) && n) {
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
flag = 0;
for (max_d = 1; max_d <= n; max_d++) {
dfs(1, 0);
if (flag) break;
}
}
return 0;
}