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;

}

 

你可能感兴趣的:(SSI)