1. 程式人生 > >BZOJ2121 字符串遊戲 【dp】

BZOJ2121 字符串遊戲 【dp】

bzoj 匹配 -- 如何 斷點 print space %d const

題目鏈接

BZOJ2121

題解

dp怎麽那麽神吶QAQ

我們要求出最小字符串長度
我們設一個\(dp[i]\)表示前\(i\)個字符最後所形成的最短字符串長度
對於第\(i\)個字符,要麽保留,就是\(dp[i] = dp[i - 1]\),要麽和前面若幹個字符一起被刪掉

我們設\(c[i][j]\)表示區間\([i,j]\)能否被刪掉
如果我們能求出\(c[i][j]\)就好了

我們再設一個\(f[i][j][k][t]\)表示區間\([i,j]\)能否匹配第\(k\)個串的前\(t\)個字符
如果存在一個\(k\),使得\(f[i][j][k][len[k]]\)為真,那麽\(c[i][j]\)

就為真

所以我們只需考慮如何求出\(f[i][j][k][t]\)
就是簡單的區間\(dp\)
枚舉斷點\(d\),然後\(f[i][j][k][l] |= f[i][d][k][t] \&\& c[d + 1][j]\)

註意轉移的順序,因為\(f\)\(c\)是同時計算的,倒序枚舉即可

然後就做完啦

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); using namespace std; const int maxn = 155,maxm = 100005,INF = 1000000000; int f[maxn][maxn][32][23],dp[maxn],c[maxn][maxn],len[32
],n,m; char S[maxn],s[32][23]; int main(){ scanf("%s",S + 1); m = strlen(S + 1); scanf("%d",&n); REP(i,n) scanf("%s",s[i] + 1),len[i] = strlen(s[i] + 1); for (int i = m; i; i--){ for (int j = i; j <= m; j++){ for (int k = 1; k <= n; k++){ f[i][i - 1][k][0] = 1; for (int l = 1; l <= len[k]; l++){ f[i][j][k][l] = (f[i][j - 1][k][l - 1] && S[j] == s[k][l]); for (int d = i; d < j; d++) f[i][j][k][l] |= (f[i][d][k][l] && c[d + 1][j]); } } for (int k = 1; k <= n; k++) c[i][j] |= f[i][j][k][len[k]]; } } for (int i = 1; i <= m; i++){ dp[i] = dp[i - 1] + 1; for (int j = 1; j <= i; j++) if (c[j][i]) dp[i] = min(dp[i],dp[j - 1]); } printf("%d\n",dp[m]); return 0; }

BZOJ2121 字符串遊戲 【dp】