[題解] 石家莊二中集團 3.22 日模擬賽 T1 <folding >合併字串
阿新 • • 發佈:2021-08-12
題面
Bill想要進行對一串字元進行壓縮,使其使用的字元長度最短。
例如, \(AAAAAAAAAABABABCCD\) 可以壓縮成 \(10(A)2(BA)B2(C)D\).
\(“( )”\) 算2個字串長度
\(n\leq100\)
題解
- 首先一看,\(DP\) 題,而且是區間 \(DP\)
傳統的 \(DP\) 在這裡肯定是不行的,涉及到求前面係數的長度,因此我們肯定是要寫一個 \(check\) 計算函式的。
這題的複雜度允許我們列舉迴圈節的長度,這兩條可以合併。
int calc(int l, int r, int k) { for (int i = 0; i <= r - l; ++ i) { if (str[l + i] != str[l + i % k]) return 2147483647; } if ((r - l + 1) / k == 100) return 5 + f[l][l + k - 1]; else if ((r - l + 1) / k >= 10) return 4 + f[l][l + k - 1]; else return 3 + f[l][l + k - 1]; }
看完這段程式碼,我發現題面有 BUG。
-
看一下這段輸入
PPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBPPPPPQQQQQPPPPPQQQQQAAAAABBBBBAAAAABBBBBCCCCCDDDDDCCCCCDDDDD
我們發現括號是可以巢狀的。
因此這麼寫就沒問題了。
總程式碼:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 110; const int INF = 0x3f3f3f3f; char str[maxn]; int n,f[maxn][maxn]; void CheckMin(int &X, int Y) { X > Y ? X = Y : 0; } int calc(int l, int r, int k) { for (int i = 0; i <= r - l; ++ i) { if (str[l + i] != str[l + i % k]) return 2147483647; } if ((r - l + 1) / k == 100) return 5 + f[l][l+k-1]; else if ((r - l + 1) / k >= 10) return 4 + f[l][l+k-1]; else return 3 + f[l][l+k-1]; } void check(int l, int r){ for (int i = 1; i <= r - l + 1; ++ i) { if ((r - l + 1) % i == 0) { int val = calc(l, r, i); CheckMin(f[l][r], val); } } } int main(){ //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); cin>>(str+1); n=strlen(str+1); for(int len=1;len<=n;len++){ for(int i=1;i<=n;i++){ int j=i+len-1; if(j>n)break; f[i][j]=len; check(i,j); for(int k=i;k<j;k++){ CheckMin(f[i][j],f[i][k]+f[k+1][j]); } } } printf("%d",f[1][n]); return 0; }
後記與反思
-
進行各種 \(DP\) 時,一定要弄清楚我要維護什麼狀態,多少狀態
-
理清思路再寫程式碼,思路是最重要的。
-
區間 DP 有時 區間長度 要從 1 開始!!!
\(return \ 0\)