#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int n, a, b, c;
    cin >> n >> a >> b >> c;
    vector<int> color(n);
    for(int i = 0; i < n ; ++i){
        cin >> color[i];
    }
    vector<vector<int>> dp(n, vector<int>(n, 0)); // dp[i][j]索引i到j的最大得分
    // 初始化窗口为1
    for(int i = 0; i < n; ++i){
        dp[i][i] = a;
    }   
    // 初始化窗口为2
    for(int i = 0; i < n - 1; ++i){
        int j = i + 1;
        if(color[i] == color[j]){
            dp[i][j] = max(2*a, b);
        }else {
            dp[i][j] = 2*a;
        }
    }
    // 计算窗口>=3 的情况
    for(int len = 3; len <= n; ++len){
        for(int i = 0; i < n - len + 1; ++i){

            int j = i + len - 1;
            // 无条件拆分
            for(int k = i; k < j; ++k){
                dp[i][j] = max(dp[i][k] + dp[k + 1][j], dp[i][j]);
            }
            // 有条件拆分:相同颜色色块可合并拆分
            for(int k = i + 1; k < j; ++k){
                if(color[i] == color[j]){ // 区间两端颜色相同
                    if(color[k] == color[i]){
                        // 三色相同
                        dp[i][j] = max(dp[i][j], dp[i + 1][k - 1] + dp[k + 1][j - 1] + max({3*a, a + b, c}));
                    }else {
                        dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + max(2*a, b));
                    }
                }else { // 区间两端颜色不同
                    if(color[k] == color[i]){
                        dp[i][j] = max(dp[i][j], dp[i + 1][ k - 1] + max(2*a, b) + dp[k + 1][j]);
                    }
                    if(color[k] == color[j]){
                        dp[i][j] = max(dp[i][j], dp[i][k - 1] +max(2*a, b) + dp[k + 1][j - 1]);
                    }
                }
            }
        }
    }

    cout << dp[0][n - 1] << endl;
    return 0;
}
// 64 位输出请用 printf("%lld")

一、解题思路

1、人话版:当你知道由一个方块组成区间的奖励最大值,你就能算出由两个方块组成区间的奖励最大值,聪明的你,开始以此类推……

dp[i][j]含:消除 索引i到索引j位置的色块 可以获得的最大分值。

(1)单独消除每个方块的值固定 dp[i][i] = a;

(2)俩俩一组,看看一块杀俩分值大,还是一个一个杀分值大 (具体看游戏设计师的脑回路了,万一连续消除两个不给你分呢~)

(3)三个及以上一组,例如四个方块可以分成(1 + 3,2 + 2, 3 + 1)组合:

无条件遍历:别管有没有相同色块,咱就按顺序硬消

有条件遍历:先把端点中间多余的方块杀喽,再把端点处相同色块凑一起杀。

2、思想总结:从小区间的最优解,递推更大区间的最优解,无条件拆分(不合并相同颜色方块) + 有条件拆分(合并相同颜色方块)

3、备注:最开始我还初始化了窗口大小为三的情况:

// 初始化窗口为3
for(int i = 0; i < n - 2; ++i){
    int j = i + 2;

    if(color[i] == color[j] && color[i] == color[i + 1]){
        // 三个同色
        dp[i][j] = max({3*a, a + b, c});
    }else if(color[i] == color[j]){

        dp[i][j] = max(3*a, a + b);
    }else{
        // 拆成左一右二,或左二右一
        dp[i][j] = max(dp[i][i] + dp[i + 1][j],dp[i][i + 1] + dp[j][j]);
    }
}

后来发现这里的逻辑冗余,所以长度为3的时候,如 i = 0, k = 1, j = 2,出现类似于dp[i + 1][ k - 1] = dp[1][0] ,一维索引比二维索引大时初始值为0(但这么操作的话,dp初始值只能是0, 负值会影响结果),不会越界。