『Blocks 区间dp』


Blocks

Description

Some of you may have played a game called 'Blocks'. There are n blocks in a row, each box has a color. Here is an example: Gold, Silver, Silver, Silver, Silver, Bronze, Bronze, Bronze, Gold. The corresponding picture will be as shown below:

5cfe457bce4d064446.jpg

If some adjacent boxes are all of the same color, and both the box to its left(if it exists) and its right(if it exists) are of some other color, we call it a 'box segment'. There are 4 box segments. That is: gold, silver, bronze, gold. There are 1, 4, 3, 1 box(es) in the segments respectively.

Every time, you can click a box, then the whole segment containing that box DISAPPEARS. If that segment is composed of k boxes, you will get kk points. for example, if you click on a silver box, the silver segment disappears, you got 44=16 points.

Now let's look at the picture below:

5cfe45917281883900.jpg

The first one is OPTIMAL.

Find the highest score you can get, given an initial state of this game.

题意:通过点击某一颜色消除相邻的所有的这种颜色,得分为len*len,求最大分

Input Format

第一行为一个整数 N。

第二行为 A1,A2,…,AN。

Output Format

一行一个整数,代表最大价值。

Sample Input

9
1 2 2 2 2 3 3 3 1

Sample Output

29

解析

这种区间操作类的最优解问题显然是区间\(dp\),不过这道题的状态有点棘手。

对于一次消除操作,可能会带来左右两边原本不相邻的部分合并带来的影响,所以通常来说的状态是不行了。我们考虑设计一种状态能够记录这种合并带来的影响:设\(f[l][r][k]\)代表区间消除\([l,r]\),序列后面通过消除操作使得有\(k\)个颜色为\(a[r]\)的块跟在区间\([l,r]\)后面的最大得分。

考虑转移,显然,我们可以直接将后面跟着的\(k\)个块和第\(r\)个块连在一起消掉,这是一种转移方式。还有就是我们可以在区间\([l,r-1]\)中枚举一个颜色与\(a[r]\)相同的点\(i\),然后将区间\([i+1,r-1]\)作为一个子问题直接消去,这样就使得\(a[r]\)加入后面跟着的\(k\)个块中,\(i\)成为右端点,利用这个状态转移即可。

至于如何枚举区间内和\(a[r]\)颜色相同的点呢?这是可以预处理直接向前查找的。

\(Code:\)

#include<bits/stdc++.h>
using namespace std;
const int N = 220;
int n,a[N],pre[N],last[N];
int f[N][N][N];
inline void input(void)
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
}
inline void init(void)
{
    for (int i=1;i<=n;i++)
    {
        pre[i] = last[a[i]];
        last[a[i]] = i;
    }
}
inline int dp(int l,int r,int k)
{
    if ( l > r ) return 0;
    if ( f[l][r][k] ) return f[l][r][k];
    f[l][r][k] = dp( l , r-1 , 0 ) + (k+1) * (k+1);
    for (int i=pre[r];i>=l;i=pre[i])
        f[l][r][k] = max( f[l][r][k] , dp( l , i , k+1 ) + dp( i+1 , r-1 , 0 ) );
    return f[l][r][k];
}
int main(void)
{
    input();
    init();
    memset( f , 0 , sizeof f );
    dp( 1 , n , 0 );
    printf("%d\n",f[1][n][0]);
    return 0;
}

转载于:https://www.cnblogs.com/Parsnip/p/10999917.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值