没有description
可以比较显然的想出$dp_{i,j}$表示$i$到$j$全部消除的步数作为状态
但是好像不大好转移x
于是就考虑在状态里多记录一点信息
$dp_{i,j,k,l}$表示$i$到$j$消除到剩下第$k$串的前$l$位的最小步数
再设$Can_{i,j}$表示$i$到$j$全部消除的一个最小步数
显然,$Can_{i,j}=min(dp_{i,j,k,Len[k[})+1$
于是我们只需要考虑$dp$数组的转移即可
$dp$数组的转移有两种
一种是直接往后匹配一位 即$dp_{i,j,k,l}$=$min(dp_{i,j-1,k,l-1})$条件是右端点的字符和第$l$位相同
还有一种转移是枚举段点$m$
段点前的被消成第$k$串的前$l$位,段点后全部被消除
即$dp_{i,j,k,l}=min(dp_{i,m-1,k,l}+Can_{m,j})$
接下来我们就得到了一个$Can$数组
再定义$f_{i,j}$表示前$i$个字符,用了$j$次魔法的最小保留
然后直接转移即可
Code:
#pragma GCC optimize("Ofast") #pragma GCC optimize("inline", "fast-math", "unroll-loops", "no-stack-protector") #pragma GCC diagnostic error "-fwhole-program" #pragma GCC diagnostic error "-fcse-skip-blocks" #pragma GCC diagnostic error "-funsafe-loop-optimizations" #include <bits/stdc++.h> using namespace std; int N,M,K; char B[55][55]; int Len[205]; int Can[205][205],dp[201][201][21][11]; int ans[205][205]; int main(){ freopen("string.in","r",stdin); freopen("string.out","w",stdout); char A[505]; scanf("%d%d%d",&N,&M,&K); scanf("%s",A+1); memset(dp,63,sizeof(dp)); memset(Can,63,sizeof(Can)); for(int i=1;i<=N;i++){ Can[i][i-1]=0; for(int j=1;j<=M;j++) dp[i][i-1][j][0]=0; } for (int i=1;i<=M;i++) scanf("%s",B[i]+1),Len[i]=strlen(B[i]+1); for(int len=1;len<=N;len++){ for(int l=1;l<=N-len+1;l++){ int r=l+len-1; for(int i=1;i<=M;i++){ for(int j=1;j<=Len[i];j++) if(A[r]==B[i][j]) dp[l][r][i][j]=min(dp[l][r][i][j],dp[l][r-1][i][j-1]); for(int j=0;j<=Len[i];j++) for(int k=l;k<=r;k++) if (Can[k][r]) dp[l][r][i][j]=min(dp[l][k-1][i][j]+Can[k][r],dp[l][r][i][j]); } for(int i=1;i<=M;i++) Can[l][r]=min(Can[l][r],dp[l][r][i][Len[i]]+1); } } for (int i=1;i<=N;i++) for (int j=1;j<=K;j++) ans[i][j]=1e9; for (int i=1;i<=N;i++) ans[i][0]=i; ans[1][1]=1; for (int i=1;i<=N;i++) for (int j=1;j<=K;j++){ ans[i][j]=ans[i-1][j]+1; for (int k=1;k<i;k++) if (j>=Can[k][i]) ans[i][j]=min(ans[i][j],ans[k-1][j-Can[k][i]]); } int anss=1e9; for (int i=1;i<=K;i++) anss=min(anss,ans[N][i]); cout<<anss; return 0; }