UVA 10559 Blocks(区间DP&&递推)

题目大意:给你玩一个一维版的消灭星星,得分是当前消去的区间的长度的平方,求最大得分。

现在分析一下题目

因为得分是长度的平方,不能直接累加,所以在计算得分时需要考虑前一个状态所消去的长度,仅用dp[l][r]来表示区间最大得分是不足以用来转移的。

我们的解决方法是:增加一维预测未来可能会出现的情况,用dp[l][r][t]表示区间[l,r]之后附加t个与r同色的方块时所能得到的最大值。为了降低时间复杂度,我们可以在读入时处理一下,将一段长度为a、颜色为b的区间k表示成一个len[k]=a,col[k]=b的“点”。这样,当我们计算dp[l][r][0]时,首先尝试直接消除区间r,得到dp[l][r][0]=dp[l][r-1][0]+(len[r])^2,接着,将它之后所有可能的附加t个同色方块的情况都算出来:dp[l][r][t]=dp[l][r-1][0]+(len[r]+t)^2。之后,在[l,r-1]之间寻找可能与区间r同色的区间k,若col[r]==col[k],则尝试消除区间r与k之间的所有方块,让区间r与k并在一起计算分数并更新所有dp[l][r][t]的值:dp[l][r][t]=max(dp[l][r][t],dp[l][k][len[r]+t]+dp[k+1][r-1][0])。最后dp[1][n][0]即为答案。(n为一开始同色区间的数量)

代码:(140ms,用递推速度就是不一样,比记搜不知道高到哪里去了)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int col[210],len[210],dp[210][210][210];
int sum[210];
//by sclbgw7
inline int sq(int x)
{return x*x;}

inline int maxn(int x,int y)
{return x>y?x:y;}

int main()
{
    int T,time;
    scanf("%d",&T);
    for(time=1;time<=T;++time)
    {
        int n=0,m,t1;//m为方块个数,n为区间个数
        scanf("%d",&m);
        for(int i=1;i<=m;++i)
        {
            scanf("%d",&t1);
            if(t1==col[n])
              ++len[n];
            else
            {
                col[++n]=t1;
                len[n]=1;
            }
        }
        for(int i=1;i<=n;++i)
          sum[i]=sum[i-1]+len[i];//sum为前缀和,用于限定预测t的范围
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i)
          for(int l=1;l+i<=n;++l)
            {
                int r=l+i,re=sum[n]-sum[r];//re为当前t可能用到的最大值
                for(int t=0;t<=re;++t)
                  dp[l][r][t]=dp[l][r-1][0]+sq(t+len[r]);
                for(int k=r-1;k>=l;--k)
                  if(col[k]==col[r])
                    for(int t=0;t<=re;++t)
                      dp[l][r][t]=maxn(dp[l][r][t],dp[l][k][len[r]+t]+dp[k+1][r-1][0]);
            }
        printf("Case %d: %d\n",time,dp[1][n][0]);
        //神坑!!!!!冒号之后要加一个空格!!!!!!!
    }
    return 0;
}

 

posted @ 2018-01-08 21:25  sclbgw7  阅读(102)  评论(0编辑  收藏  举报