返回顶部

AcWing 187. 导弹防御系统 (dfs,贪心,LIS)

  • 题意:给你一组数,问你最少能凑多少个LIS和LDS.

  • 题解:能解决的办法好像只有dfs找了,但是我们可以贪心和剪枝来优化,我们来看贪心,当我们遍历到\(i\)个数时,想要把它加入到前面的某个上升子序列中,那么最优的放法一定是放到末尾数字比\(a[i]\)小的最大的那个数后面,因为加入放到一个末尾数字很小的子序列后面,肯定是浪费的.然后照这个贪心思路就可以dfs了.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
     
    int n;
    int a[N];
    int up[N],down[N];
    int ans;
    
    //pos表示当前位置,u表示当前上升序列的个数,d表示当前下降序列的个数
    void dfs(int pos,int u,int d){  
        if(u+d>=ans) return;  //剪枝
        if(pos==n){
            ans=u+d;
            return;
        }
    
        int k;
        //找到第一个末尾比当前位置小的上升序列,如果没有就新建一个新的上升序列
        //这里一定保证第一个满足条件的是最优的(即末尾最大),因为第x+1个序列的末尾一定比第x个小
        //自己可以写几个例子看看
        for(k=1;k<=u;++k) if(up[k]<a[pos]) break; 
        int tmp=up[k];
        up[k]=a[pos];
        dfs(pos+1,max(u,k),d);
        up[k]=tmp;  //dfs回溯,不再赘述
    
        for(k=1;k<=d;++k) if(down[k]>a[pos]) break;
        tmp=down[k];
        down[k]=a[pos];
        dfs(pos+1,u,max(d,k));
        down[k]=tmp;
    
    }
     
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        while(cin>>n,n!=0){
           rep(i,1,n){
               cin>>a[i];
           } 
    
           ans=n;
    
           dfs(1,0,0);
    
           cout<<ans<<'\n';
    
        }
     
        return 0;
    }
    
posted @ 2021-04-09 00:29  Rayotaku  阅读(45)  评论(0编辑  收藏  举报