1. 程式人生 > 實用技巧 >2020.7.27考試D1T2:方塊消除(Block)

2020.7.27考試D1T2:方塊消除(Block)

注意!!!!傳送門題目與本題解略有不符
區別在於題解為多組資料捆綁測試,傳送門題目為單組資料測試
題目傳送門

D1T2:方塊消除(Block)

Description
Jimmy最近迷上了一款叫做方塊消除的遊戲. 遊戲規則如下:n個帶顏色方格排成一列,相同顏色的方塊連成一個區域(如果兩個相鄰的方塊顏色相同,則這兩個方塊屬於同一個區域). 遊戲時,你可以任選一個區域消去.設這個區域包含的方塊數為x,則將得到x^2的分值.方塊消去之後,右邊的方格將向左移動.
雖然遊戲很簡單,但是要得到高分也不是很容易.Jimmy希望你幫助他找出最高可以得到多少分.
Input
測試包括多個數據,數字給出一個數字T,表示有多少組資料。T<=15對於每組資料,先給出一個數字N,1<=n<=200。表示有多少個方塊,接下來一行N個數字表示每個方塊的顏色,其值在1到N之間。
Output
對於每組資料,輸出最大得分。格式如樣例。
Sample Input
2
9
1 2 2 2 2 3 3 3 1
1
1
Sample Output
Case 1: 29
Case 2: 1

思路

這道題目表述的有一點不清晰(也可能是我理解能力太差了吧)
但大概就是:
T組資料,做T次
資料有n個數字,表示n個方塊中,每個方塊顏色
相鄰的相同顏色的方塊看做一組
不停刪除任意一組相鄰的相同顏色的方塊
刪除後,如果刪除掉那組左右兩邊方塊顏色相同便視為一組
刪除時,每次刪除區域長度平方的和

然後我們會想到用DP來解題
因為我們求刪除區域長度平方的和,實質是求如何構成最長的區間長度

所以這是一道區間DP題目

而在考試時,因為種種原因 (不熟悉區間DP)
推匯出的是有關樹上的搜尋題目
又沒有剪枝和記憶化,所以超時

實際上,區間DP就是類似於向下劃分,然後向上遞推的某些樹形結構

通常用劃分區間的方法

區間長度DP狀態,一個區間的狀態包含於該狀態的更小區間得來

所以,本題也是這樣的
消去一個區域,就會得到區域所含個數b[i]^2的值
並且消去該區域後,會使本來與被消去區域相鄰的兩個區域相鄰

考試沒有用DP還有一個重要的原因:
因為當前區域[l,r]的相鄰的兩個區域還可能在當前區域刪去後合併
而如果只考慮區間[l,r]能得到的最大收益,一定是不全面

所以在考後研究發現需要再引入一維,作為dp[i][j][k]
表示:i到j的區域,右邊有k個和a[i]接在後面最大收益

兩種決策:

  1. 直接刪除[i,j,k]這段區間,dp[i][j][k]=dp[i][j-1][0]+(a[j]+k)^2
  2. 在[i,j]區間中,尋找一個p連續方塊,使得p與j是同一種類型的,那麼可以,把p,j之間的方塊刪除後再進行刪除,dp[i][j][k]=dp[i][p][len[j]+k]+dp[p+1][j-1][0]

然後對每個i,j遍歷一遍所有的取法取最大值即可

注意在每組資料任務完成後初始化陣列

程式碼

#include<bits/stdc++.h>
using namespace std;
int n;
int a[1010000];
int b[1010000];
int dp[100][100][100];
int s[1010000];
int main(){
	int t;
	cin>>t;
	for(int T=1;T<=t;T++){
		cin>>n;
	    for(int i=1;i<=n;i++){
	        cin>>a[i];
	    }
	    for(int i=1;i<=n;i++){
	        cin>>b[i];
	        s[i]=s[i-1]+b[i];
	    }
	    
	    //注意:這裡的l指文中的i,r指j,k保持不變 
	    for(int i=0;i<=n;i++){
	        for(int l=1;l<=n;l++){
	            int r=l+i;
	            if(i+l>n){
	                break;
	            }
	            for(int k=0;k<=s[n]-s[r];k++){
	                dp[l][r][k]=dp[l][r-1][0]+(b[r]+k)*(b[r]+k);
	            }
	            for(int k=l;k<r;k++){
	                for(int j=0;j<=s[n]-s[r];j++){
	                    if(a[k]==a[r]){
	                        dp[l][r][j]=max(dp[l][r][j],dp[l][k][b[r]+j]+dp[k+1][r-1][0]);
	                    }
	                }
	            }
	        }
	    }
	    cout<<"Case "<<T<<" "<<dp[1][n][0]<<endl;
	    memset(dp,0,sizeof(dp));
	}
    return 0;
}