poj1390 Blocks(經典區間dp/消除問題)

題目

T(T<=15)組樣例,每次給出n(n<=200)個方塊,第i個整數ci代表方塊的顏色(1<=ci<=n),

每次你可以手動消除一段連續的長度爲x的方塊,並獲得這段方塊帶來的價值x^{2}

消除之後,後面的方塊會跟上來,但並不會產生消除的連鎖反應,需要你指定纔會發生消除

求消完所有方塊後的最大價值之和

思路來源

https://www.cnblogs.com/Tieechal/p/11637638.html 清楚明白的講解

題解

經典區間dp消除問題的例題,也是入坑消除問題的第一道題,定義的狀態很神奇

[Luogu2135] 方塊消除UVA10559 Blocks,一道題出了三遍

方塊消除不用尺取把相同顏色的合在一起,而Blocks需要

 

dp[l][r]並不能解決問題,因爲可能先消r後面的一段,使和r相同的顏色緊挨着r,這樣的決策,使得dp[l][r]不能固定下來

因此多開一維,dp[l][r][lx]代表對於[l,r]區間,當前有lx個方塊和b[r]顏色相同,緊跟在r後面沒有被消除

 

本次決策有兩種,決策是基於r這種顏色的,

一種是本次利用r的價值,把(lx+b[r])個消掉,遞歸到dp[l][r-1][0]的情形,

另一種爲了以後更大的r的價值,是從[l,r-1]中找到一個和r顏色相同的位置i,先消掉[i,r-1]這段,從而把(lx+b[r])個續到i後面去

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=205;
int t,n,m,a[N],b[N],v[N],dp[N][N][N];
int dfs(int l,int r,int lx){
	int &ans=dp[l][r][lx];
	if(~ans)return ans;
	int all=b[r]+lx;
	if(l==r){
		return ans=all*all;
	}
	ans=0;
	ans=max(ans,dfs(l,r-1,0)+all*all);
	for(int i=l;i<r;++i){
		if(v[i]==v[r]){
			ans=max(ans,dfs(i+1,r-1,0)+dfs(l,i,all));
		}
	}
	return ans;
}
int main(){
	scanf("%d",&t);
	for(int ca=1;ca<=t;++ca){
		memset(dp,-1,sizeof dp);
		scanf("%d",&n);
		m=0;
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			if(a[i]!=a[i-1])b[++m]=1,v[m]=a[i];
			else b[m]++;
		}
		printf("Case %d: %d\n",ca,dfs(1,m,0));
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章