Loading [MathJax]/jax/output/CommonHTML/jax.js

[题解]P4302 [SCOI2003] 字符串折叠

思路

区间 DP 好题。定义 dpi,j 表示将 sij 折叠能获得的最短长度。

那么,依旧是枚举一个中间点 k,那么,我们将 dpi,j 分为了 dpi,kdpk+1,j 两部分。

对于这两部分,可显然,有:dpi,j=min(dpi,k+dpk+1,j)

如果可以将 [i,j] 区间合并,可以枚举折叠后剩余的字符串的最后一位的下标 k,然后判断一下如果是 k,能否折叠,如果可以,则有:dpi,j=min(dpi,i+k1+numcnt+2)

其中,cnt 表示折叠后字符串中的数字,numi 表示 i 数字的长度,2 表示括号。

递推边界显然,对于所有的 dpi,i=1

Code

#include <bits/stdc++.h>  
#define re register  
  
using namespace std;  
  
const int N = 110,inf = 0x3f3f3f3f;  
int n;  
int num[N];  
int dp[N][N];  
string s;  
  
inline void init(){  
    memset(dp,inf,sizeof(dp));  
    for (re int i = 1;i <= 9;i++) num[i] = 1;  
    for (re int i = 10;i <= 99;i++) num[i] = 2;  
    num[100] = 3;  
    for (re int i = 1;i <= n;i++) dp[i][i] = 1;  
}  
  
inline bool check(int l,int r,int x){  
    for (re int i = l + x;i <= r;i++){  
        int id = l + (i - l) % x;  
        if (s[i] != s[id]) return false;  
    }  
    return true;  
}  
  
int main(){  
    ios::sync_with_stdio(0);  
    cin.tie(0);  
    cout.tie(0);  
    cin >> s;  
    n = s.length();  
    s = ' ' + s;  
    init();  
    for (re int l = 2;l <= n;l++){  
        for (re int i = 1;i + l - 1 <= n;i++){  
            int j = i + l - 1;  
            for (re int k = i;k < j;k++) dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j]);  
            for (re int k = 1;k < l;k++){  
                if (l % k == 0){  
                    int cnt = l / k;  
                    if (check(i,j,k)) dp[i][j] = min(dp[i][j],dp[i][i + k - 1] + num[cnt] + 2);  
                }  
            }  
        }  
    }  
    cout << dp[1][n];  
    return 0;  
}  

作者:WaterSun

出处:https://www.cnblogs.com/WaterSun/p/18268801

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   WBIKPS  阅读(7)  评论(0编辑  收藏  举报
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示