傳送門c++
多圖警告!!!git
一種很新奇的\(DP\),全網彷佛只有一兩篇題解……優化
首先,序列中的一段\(e\)等價於在跳的過程當中這一段\(e\)以後的一個字符必需要通過,而且在最後的答案中加上$2 \times $e的個數。spa
那麼原題等價於:給出一個序列和兩種移動方式,移動過程當中必需要通過某一些點,求最小代價。設計
咱們不妨把若干連續的\(f\)操做和若干連續的\(h\)操做當作線,那麼移動路線就變成下面這樣code
首先,考慮下面兩種移動路線blog
A路線必定沒有B路線優,由於A路線有重複的折返。字符串
這樣說來:若是通過某些連續的\(f\)操做以後開始進行\(h\)操做,那麼必定會到達要到達的最前面的目標,而後一直進行\(f\)操做再也不回來。get
到這裏不難設計出一個暴力的\(DP\):設\(dp_{i,j}\)表示已經通過了前\(i\)個必經字符,當前光標在第\(j\)個字符時的最小代價。設字符集爲\(A\),那麼這種\(DP\)是\(O(N^2A)\)的,不夠優秀。考慮優化。it
發現上面的條件等價於對於某一個位置\(i\),通過的位置覆蓋了位置\(i\)與\(i+1\)之間的線段的線的數量要麼是\(1\),要麼是\(3\),對應下圖的\(AB\)兩種狀況。
到了這裏就能夠開始設計更加優秀的\(DP\)了
設\(p_{i,j}\)表示覆蓋了\(i\)與\(i+1\)之間的線段\(1\)次,且覆蓋\(i\)與\(i+1\)之間的線段的\(f\)操做選擇的字符是\(j\)的最小代價,\(q_{i,j,k}\)表示覆蓋了\(i\)與\(i+1\)之間的線段\(3\)次,且在進行\(h\)操做以前覆蓋\(i\)與\(i+1\)之間的線段的\(f\)操做選擇的字符是\(j\)、在進行\(h\)操做以後覆蓋\(i\)與\(i+1\)之間的線段的\(f\)操做選擇的字符是\(k\)的最小代價
又設\(s_i\)表示字符串的第\(i\)個字符,\(imp_i\)表示原串中第\(i\)個字符前是否存在字符\(e\)
轉移:
\[\begin{align}p_{i,j} = & p_{i-1,j} & j \neq s_i \&\& imp_i \neq 1\\& p_{i-1,s_i} + 2 \\& q_{i-1,s_i,j} & j \neq s_i \\ & q_{i-1,s_i,s_i} + 2 \end{align}\]
\(p_{i,j}\)的轉移分別對應下圖的\(ABCD\)狀況
其中虛線表示新加入的線,紅色字表示對應位置的字符類型,黑色字表示位置編號
\(\begin{align} q_{i,j,k} = & p_{i-1,j} + 3 & j \neq s_i \\ & p_{i-1,s_i}+5 \\ & q_{i-1,j,k} + 1 & j \neq s_i \&\& k \neq s_i \\ & q_{i-1,s_i,k} + 3 & k \neq s_i \\ & q_{i-1,j,s_i} + 3 & j \neq s_i \\ & q_{i-1,s_i,s_i} + 5 \end{align}\)
\(q_{i,j,k}\)轉移分別對應下圖中的\(ABCDEF\)狀況
能夠發現轉移就是把線延長和補全的過程,因此叫作線頭DP
初始值:\(f_{0,s_1}=0\),其餘等於\(inf\)。最後的答案是\(f_{len,x}\),其中\(x\)是沒有在字符串中出現過的字符。這能夠理解成在無限遠的地方有一個字符\(x\),最後一次操做就是直接跳到這一個無限遠的地方。固然,這意味着最後的答案會加上跳到這個無限遠的地方的\(2\)的代價,減掉\(2\)就好了。
Update:轉移\(q\)的時候並不知道爲何D有用,可是不轉移會WA
#include<bits/stdc++.h> #define INF 0x3f3f3f3f //This code is written by Itst using namespace std; inline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == '-') f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } const int MAXN = 7e4 + 7 , A = 11; int f[MAXN][A] , g[MAXN][A][A] , ch[MAXN]; bool must[MAXN]; int N , M , cnt; inline char getc(){ char c = getchar(); while(!islower(c)) c = getchar(); return c; } int main(){ #ifndef ONLINE_JUDGE //freopen("in","r",stdin); //freopen("out","w",stdout); #endif N = read(); bool ife = 1; for(int i = 1 ; i <= N ; ++i){ char c = getc(); if(c == 'e') cnt += (ife = 1); else{ must[++M] = ife; ife = 0; ch[M] = c - 'a'; } } for(int i = 0 ; i < A ; ++i){ for(int j = 0 ; j < A ; ++j) g[0][i][j] = INF; f[0][i] = INF; } f[0][ch[1]] = 0; for(int i = 1 ; i <= M ; ++i) for(int j = 0 ; j < A ; ++j){ int t = INF; if(j != ch[i] && !must[i]) t = min(t , f[i - 1][j]); t = min(t , f[i - 1][ch[i]] + 2); if(j != ch[i]) t = min(t , g[i - 1][ch[i]][j]); t = min(t , g[i - 1][ch[i]][ch[i]] + 2); f[i][j] = t; for(int k = 0 ; k < A ; ++k){ t = INF; if(j != ch[i]) t = min(t , f[i - 1][j] + 3); t = min(t , f[i - 1][ch[i]] + 5); if(j != ch[i] && k != ch[i]) t = min(t , g[i - 1][j][k] + 1); if(j != ch[i]) t = min(t , g[i - 1][j][ch[i]] + 3); if(k != ch[i]) t = min(t , g[i - 1][ch[i]][k] + 3); t = min(t , g[i - 1][ch[i]][ch[i]] + 5); g[i][j][k] = t; } } cout << f[M][10] + 2 * cnt - 2; return 0; }