POJ 1390 Blocks(DP + 思维)题解

题意:有一排颜色的球,每次选择一个球消去,那么这个球所在的同颜色的整段都消去(和消消乐同理),若消去k个,那么得分k*k,问你消完所有球最大得分

思路:显然这里我们直接用二位数组设区间DP行不通,我们不能表示出“合并”这种情况。我们先把所有小块整理成连续的大块。

我们用click(l,r,len)表示消去l到r的所有大块和r后len块和r颜色一样的小块的最大得分。那么这样我们可以知道,click(l,r,len)只有两种情况:

1.r直接和后面len全都消去

2.r带着len先和前面的一样的颜色的一起消

代码:

#include<cmath>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 200 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
int num[maxn], a[maxn], p[maxn], cnt;
int dp[maxn][maxn][maxn];
//j后还有k个一样的小块
int click(int l, int r, int len){
    if(l > r) return 0;
    if(dp[l][r][len]) return dp[l][r][len];
    if(l == r) return dp[l][r][len] = (num[l] + len) * (num[l] + len);
    dp[l][r][len] = click(l, r - 1, 0) + (num[r] + len) * (num[r] + len);
    for(int i = l; i < r; i++){
        if(p[i] != p[r]) continue;
        dp[l][r][len] = max(dp[l][r][len], click(l, i, num[r] + len) + click(i + 1, r - 1, 0));
    }
    return dp[l][r][len];
}
int main(){
    int t, ca = 1;
    scanf("%d", &t);
    while(t--){
        int n;
        scanf("%d", &n);
        memset(num, 0, sizeof(num));
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        cnt = 0;
        a[0] = -INF;
        for(int i = 1; i <= n; i++){
            if(a[i] != a[i - 1]){
                ++cnt;
                num[cnt]++;
                p[cnt] = a[i];
            }
            else{
                num[cnt]++;
            }
        }
        printf("Case %d: %d\n", ca++, click(1, cnt, 0));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KirinSB/p/10645068.html