AcWing 187. 导弹防御系统
考察:dfs+dp
思路:
很像NOI1999的导弹拦截,但是这道题的决策需要决定是加入上升子序列还是下降子序列,然后判断加入到哪一个上升子序列.
多决策问题多用dp或搜索,这道题用dp需要记录的状态有:当前第几个数,当前上升子序列个数,当前下降子序列个数,但关键是当前最优解不一定能推到全局最优解,所以需要用dfs.
在导弹拦截中,发现的性质是覆盖序列最少的严格上升子序列个数 = 最长非上升子序列长度.同理严格下降子序列个数 = 最长非下降子序列长度.dfs枚举即可,注意加上最优性剪枝:upx+downx>=ans (因为是最小步数,所以假设当前是最少的情况.)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 const int N = 55,INF = 0x3f3f3f3f; 6 int n,a[N],ans,up[N],down[N]; 7 void dfs(int idx,int upx,int downx)//上升子序列个数,下降子序列个数 8 { 9 if(upx+downx>=ans) return;//最优性剪枝:假设当前是最少的情况 10 if(idx>n) {ans = upx+downx;return;} 11 int k = 0;//找到最大的up[k]<a[idx] 12 while(k<upx&&up[k]<a[idx]) k++;//只用下降子序列掩盖需要的个数 13 int t = up[k]; 14 up[k] = a[idx]; 15 dfs(idx+1,max(k+1,upx),downx); 16 up[k] = t; 17 k = 0; 18 while(k<downx&&down[k]>a[idx]) k++;//只用上升子序列掩盖需要的个数 19 t = down[k]; 20 down[k] = a[idx]; 21 dfs(idx+1,upx,max(downx,k+1)); 22 down[k] =t ; 23 } 24 int main() 25 { 26 while(scanf("%d",&n)!=EOF&&n) 27 { 28 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 29 ans = INF; 30 dfs(1,0,0); 31 printf("%d\n",ans); 32 } 33 return 0; 34 }