Codeforces 1107E (Vasya and Binary String) (記憶化,DP + DP)
題意:給你一個長度為n的01串,和一個數組a,你可以每次選擇消除一段數字相同的01串,假設消除的長度為len,那麽收益為a[len],問最大的收益是多少?
思路:前兩天剛做了POJ 1390,和此題很相似:POJ 1390 。我們甚至可以直接套用這個題的狀態轉移方程。仍然先把01串預處理一下,把相鄰的並且數字相同的位合並成一個塊。這樣,01串就變成了若幹個相鄰的01塊了。
設dp[i][j][k]為處理第i個塊到第j個塊,並且後面有k個位和第j個塊顏色相同,設f[i]為消除長度為i的串的最大收益,那麽狀態轉移方程可以這樣寫:
1:先把後面相同的合並了:dp[i][j][k] = max(dp[i][j][k], dp[i][j - 1][0] + f[第j個塊的位的個數 + k]。
2:與前面的顏色相同的塊合並(假設塊j與塊p顏色相同):dp[i][j][k] = max(dp[i][j][k], dp[i][p][k + 第j個塊的位的個數] + dp[p + 1][j - 1][0]);
現在唯一的問題f數組怎麽求?很明顯此題並不是一次消除的長度越長越好,多次消除短的串可能獲得更高的收益。我們可以思考一下,f[i]肯定是把長度i拆成1份,2份,......i份中收益最大的情況。
設re[i][j]為把長度j拆成i份獲得的最大收益,我們可以暴力枚舉第i份的長度是多少(假設是k),然後繼續遞歸去求得把j - k拆成i - 1份的最優值來更新當前狀態。那麽f[i]就是re[1][i],re[2][i].....re[i][i]中的最優情況。
dp和re數組我們都可以記憶化,可以大幅度提高效率,31ms水過。。。
代碼:
#include <bits/stdc++.h> #define LL long long using namespace std; LL dp[110][110][110]; bool v[110][110][110], v1[110][110]; LL f[110], b[110]; LL re[110][110]; struct node{ int flag, cnt; }; node a[110]; LL get_f(int tot, int n) { if(v1[tot][n]) return re[tot][n]; if(tot == 1) { v1[tot][n] = 1; return re[tot][n] = f[n]; } else { LL ans = 0; for (int i = 1; i <= n - tot + 1; i++) { ans = max(ans, f[i] + get_f(tot - 1, n - i)); } v1[tot][n] = 1; return re[tot][n] = ans; } } int cal(int n) { LL ans = 0; f[n] = b[n]; for (int i = 1; i <= n; i++) { ans = max(ans, get_f(i, n)); } f[n] = ans; } LL solve(int l, int r, int x) { if(v[l][r][x]) return dp[l][r][x]; if(l >= r) return f[a[r].cnt + x]; LL ans = solve(l, r - 1, 0) + f[a[r].cnt + x]; for (int i = l; i < r; i++) { if(a[i].flag == a[r].flag) { ans = max(ans, solve(l, i, a[r].cnt + x) + solve(i + 1, r - 1, 0)); } } v[l][r][x] = 1; return dp[l][r][x] = ans; } int main() { int n; char s[210]; scanf("%d", &n); scanf("%s", s + 1); int now_flag = -1, now = 0, tot = 0; for (int i = 1; i <= n; i++) { if(s[i] - ‘0‘ != now_flag) { a[++tot].flag = s[i] -‘0‘; a[tot].cnt = 1; now_flag = s[i] - ‘0‘; } else { a[tot].cnt++; } } for (int i = 1; i <= n; i++) { scanf("%lld", &b[i]); } for (int i = 1; i <= n; i++) cal(i); printf("%lld\n", solve(1, tot, 0)); }
Codeforces 1107E (Vasya and Binary String) (記憶化,DP + DP)