Ybtoj 方块消除 (区间dp)

题意

给定 $m$ 接下来两行两个长为 $m$ 的序列,第一行代表的是 $m$ 个不同的区间的颜色,第二行代表的是 $m$ 个区间的长度。同颜色的方块每次可以一起消除,设消除了 $x$ 个,那么得分就是 $x^2$ 消除后余下的序列会自动合并,直至序列为空,求最大得分。

分析

观察题意,合并序列,加分,最大值,联想一下合并石子,是不是区间 $dp$ 的样子就出来了。 但是我们发现,这题不能直接令 $dp_{(i,j)} = max{(dp_{i,k} + dp_{k+1,j})$ 这样的转移,因为考虑到合并当前的小区间之后,可能新填充进来的是同色的,那么就得不偿失了,这显然是不行的。

考虑枚举最后一个方块消掉的时候,假设我们当前的区间是 $[i,j]$ ,有两种可能:直接消掉他或者留着他和左边拼起来消掉。接下来考虑较难的第二种情况,那么我们不妨令 $p$ 为从 $j$ 同色向左最远的位置, $q$ 为 $p$ 左边 $j$ 的颜色第一次出现的位置(我们此时讨论的是 $i$ $j$ 不同色,否则不会这样)。好了,那么现在有一点是可以确定下来的,就是我们一定要消掉 $[q+1,p-1]$ 这一区间,因为留着它也不会更优。至此,我们终于引出了本题的核心的核心,第三维状态: $dp(i,j,k)$ 表示原序列的右边再拼上 $k$ 个颜色与右端点 $j$ 相同的方块。

决策的过程就是要么直接删去该节点,要么就去找 $p$ $q$ 进行转移,详见代码。

本题真的很难,感性理解吧。

希望我BB了这么多真的把我自己说懂了(

#include<cstdio>
#include<queue>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<vector>
#include<string>
using namespace std;
int t,n,a[205],dp[205][205][205],dis[205]; 
int main(){
    scanf("%d",&t);
    int cnt = 0;
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)scanf("%d",a+i);    
        for(int i=n;i;--i)
            for(int j=i+1;j<=n;++j)
                if(a[i] == a[j])dis[i]++;
        for(int i=n;i;--i)
            for(int j=i;j<=n;++j){
                for(int k=i;k<j;++k)
                    if(a[k] == a[j])
                        for(int l = 0;l <= dis[k];++l)
                            dp[i][j][l] = max(dp[i][j][l],dp[k+1][j-1][0] + dp[i][k][l+1]);
                for(int p=0;p<=dis[j];++p)
                    dp[i][j][p] = max(dp[i][j][p],dp[i][j-1][0] + (p+1)*(p+1));                        
            }
        printf("Case %d: %d\n",++cnt,dp[1][n][0]);
        memset(dp,0,sizeof(dp));
        memset(dis,0,sizeof(dis));
        
    }
}

 

posted @ 2022-03-21 21:02  Xu_brezza  阅读(44)  评论(0编辑  收藏  举报