题目大意:
一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降,n个导弹求最少需要多少防御系统
题目分析:
本题就是求最少要用多少个上升或下降序列可以将所有炮弹包含
由于有的导弹可以选择上升,有的可以选择下降,不是单纯地问所存在的序列可以划分为多少组上升子序列的问题,所以不能用 导弹拦截的那种做法
我们现在考虑暴力枚举
我们需要思考的问题就转化为我们应该将p[i]放在上升序列还是下降序列
首先能否将p[i]加入某个系统中其实只与该系统拦截的最后一个导弹有关(LIS核心思想)
贪心思想:现在对于一个上升序列来说让一个系统拦截的最后一个导弹高度一定是越低越好
所以如过叫加到上升序列中 那么一定是加到所有能放入的上升序列中,最后一个元素最大的那一个。这样就可以大大的节省时间
时间复杂度:O(n2^n) 因此需要用到 迭代加深/维护全局最小值,剪枝 的优化
维护全局最小值
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=55;
int p[N];
int n;
int up[N],dw[N];
int ans;
void dfs(int u,int su,int sd){
if(su+sd>=ans) return;//剪枝,这种情况已经大于最优解 就不需要继续搜索了
if(u==n)//每个数都搜完了 并且能走到表明该情况没有被剪枝 则一定是最优解
{
ans=su+sd;
return;
}
//将第u个数字放到严格上升序列中
if(!su||p[u]<=up[su-1]){//上升序列数为零 或 p[u]不能放在任何一个上升序列中
up[su]=p[u];//建立一个新的上升序列以p[u]为端点
dfs(u+1,su+1,sd);
}
else{
int k=0;
while(k<su&&p[u]<=up[k]) k++;//找到第一个端点大于p[u]的序列
int t=up[k];//记录当前序列端点的值 用于回溯
up[k]=p[u];//更新端点值
dfs(u+1,su,sd);
up[k]=t;//回溯
}
//将第u个数字放到严格下降升序列中
if(!sd||p[u]>=dw[sd-1]){
dw[sd]=p[u];
dfs(u+1,su,sd+1);
}
else{
int k=0;
while(k<sd&&p[u]>=dw[k]) k++;
int t=dw[k];
dw[k]=p[u];
dfs(u+1,su,sd);
dw[k]=t;
}
}
int main()
{
while(cin>>n,n)
{
ans=n;
for(int i=0;i<n;i++) cin>>p[i];
dfs(0,0,0);
cout<<ans<<endl;
}
return 0;
}