【区间 dp】AcWing 322. 消木块

思路比较有新意的区间 dp。

分析

直接用 \(f(l, r)\) 表示删除区间 \([l, r]\) 的最大收益会发现无法正确处理删除后出现同色木块合并对应的贡献

那我们考虑用更复杂的状态表示来维护之。

发现一个区间的右端点终究会有一个时候被删掉,所以利用其来进行状态转移。

我们用 \(f(l, r, co)\) 表示 \([l, r]\) 区间,在 \(r\) 右边与 \(r\) 相连的同色 木块的个数为 \(co\)(即 \(\texttt{connect}\))的状态的最大收益

那么就枚举一下 \(r\) 在与前面哪个与其同色的块合并然后求个 \(\max\) 就好了,当然也不要忘了不与前面合并的情况。(详细见下面转移方程)

转移方程为:

\[f(l, r, co) = \max(f(l, r-1, 0)+(1+co)^2, \max_{k\in [l, r-1] \cap color_k=color_r} f(l, k, 1+co) + f(k+1, r-1, 0)) \]

// Problem: 消木块
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/324/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=220;

int n, w[N];
int f[N][N][N];

int dp(int l, int r, int co){
	int &res=f[l][r][co];
	if(~res) return res;
	if(l>r) return res=0;
	res=dp(l, r-1, 0)+(1+co)*(1+co);
	rep(k,l,r-1) if(w[k]==w[r]){
		res=max(res, dp(l, k, 1+co)+dp(k+1, r-1, 0));
	}
	return res;
}

int main(){
	int cs; cin>>cs;
	int ks=0;
	while(cs--){
		cout<<"Case "<<(++ks)<<": ";
		cin>>n;
		rep(i,1,n) read(w[i]);
		memset(f, -1, sizeof f);
		cout<<dp(1, n, 0)<<endl;
	}	
	return 0;
}
posted @ 2022-06-07 20:22  HinanawiTenshi  阅读(34)  评论(0编辑  收藏  举报