[GMOJ6281] 串

没有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;
}

 

posted @ 2019-10-31 15:10  si_nian  阅读(157)  评论(0)    收藏  举报