給一串01串,對該串進行若干次操做,直到串爲空ios
操做爲:選擇一段連續的0或者1,刪除它,拼接先後兩部分紅爲新串,獲得價值爲a[刪除的長度](a爲給定的數組)c++
一個很是規的DP數組
考慮題目所給的操做,咱們從中刪除一段,再把先後拼接起來,如何設置狀態?記錄下斷點的位置?不行,那樣咱們可能在其中插入不少斷點,而且不便於轉移spa
$...111000111...$code
若是咱們僅僅研究中間的子串$111000111$ci
咱們刪除中間的0,造成$111111$it
再刪除中間的1,這樣咱們其實能夠看作原串是$000\underline{111}111$io
也就是前面的1和後面的1實際上是捆在一塊兒的,這樣按原來的操做,答案不會改變class
設置狀態:$dp[i][j][k]$ 表示刪除從i到j的子串,該子串前面捆綁了$k-1$個與$s[i]$相同的字符,能獲得的最大價值搜索
這樣的話,考慮轉移方式:
咱們直接刪除前面$k$個相等的字符,$dp[i][j][k] = a[k] + dp[i+1][j][1]$,由於捆綁的全消耗掉了因此後半部分是$K = 1$
咱們從中刪除一段,再把先後粘貼起來,這樣的話,也就是上面的狀況,咱們須要把前面的和後面的捆綁起來,因此必須知足從中找到一個點$mid > i,s[i] = s[mid]$,這樣的話$dp[i][j][k] = dp[i+1][mid-1][1] + dp[mid][j][k+1]$
看這個式子,咱們只捆綁的是第一個字符,由於咱們能夠屢次進行這樣的操做,使得捆綁的是任意的(這裏不須要咱們本身去構造,而是在求解過程當中天然構造的)
因此最後的答案是$dp[1][n][1]$
因爲這個轉移比較騷,咱們能夠採用記憶化搜索
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n; char s[105]; ll a[105]; ll dp[105][105][105]; ll DP(int be,int en,int pre) { if(be > en) return 0; if(dp[be][en][pre]) return dp[be][en][pre]; if(be == en) return dp[be][en][pre] = a[pre]; ll ans = a[pre] + DP(be+1,en,1); for(int mid = be+1;mid <= en;mid++) { if(s[mid] == s[be]) ans = max(ans,DP(be+1,mid-1,1) + DP(mid,en,pre+1)); } return dp[be][en][pre] = ans; } int main() { ios::sync_with_stdio(false); cin>>n; cin>>s+1; for(int i=1;i<=n;i++) cin>>a[i]; cout<<DP(1,n,1)<<endl; }