Sicily 1685. Missile

动态规划

去年选拔赛的一个题目,题意就是给一个序列,要找出一个子序列,一增一减(第偶数个元素要比它前面的元素小,第奇数个元素要比它前面的元素大)

算是比较基础的DP,属于"第i个元素与它前面i-1的元素形成的一种关系,最后变为前i个元素的信息"

dp[i]表示加入第i个数字,与前i-1个数字能形成的最大长度,因此面对两个两个问题,第i个元素会不会加入到最终的最长子序列中,要加的话怎么加

先看方程 dp[i]=max{ dp[j] } + 1;  

若dp[j]为奇数,若想加入第i个元素,那么第i个元素将会是子序列中的第偶数个元素,那么还要满足a[i]<a[j]

若dp[j]为偶数,若想加入第i个元素,那么第i个元素将会是子序列中的第奇数个元素,那么还要满足a[i]>a[j]

因而要从1到i-1扫一次,以便找到符合要求的而且最大的dp[j]去更新dp[i]

在更新前应先初始化dp[i]=1; 表示第i个元素本身就能形成一个子序列

更新完后,若dp[i]=1,那么说明其实找不到一个符合条件的dp[j],这其实回答了我们的第一个问题   “第i个元素会不会加入到最终的最长子序列中”

最终最长的子序列,无非就是扫一次整个dp数组,找到最大值,就是我们要的答案

这种基础的DP往往付出的时间复杂度就是O(n*n),因为第i个元素与它前面所有的元素都有关系,需要一一判断

#include <cstdio>
#include <cstring>
#define N    1010
#define INF 0x3f3f3f3f
#define max(a,b) a>b?a:b

int a[N],dp[N],n,ans;

int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        for(int i=1; i<=n; i++) scanf("%d",&a[i]);
        dp[1]=1;
        if(a[2]<a[1]) dp[2]=2;
        else          dp[2]=1;

        ans=1;
        for(int i=3; i<=n; i++)
        {
            dp[i]=1; //初始化
            for(int j=i-1; j>0; j--)
                if( dp[j]&1 && a[i]<a[j]) //前j个数字中已经选出了奇数个
                    dp[i]=max(dp[i] , dp[j]+1);
                else if( !(dp[j]&1) && a[i]>a[j]) //前j个数字中已经选出了偶数个
                    dp[i]=max(dp[i] , dp[j]+1);
            ans=max(ans,dp[i]);
        } 
        printf("%d\n",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/scau20110726/p/2951228.html