给一串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; }