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 }

 

posted @ 2021-03-23 20:37  acmloser  阅读(60)  评论(0编辑  收藏  举报