P4302 [SCOI2003]字符串折叠

我居然在第一个最优解?

\(f_{i,j}\)\([i,j]\)中的最短长度。

\[f_{i,j} = \min(\min \limits_{i \le k < j} \{f_{i,k} + f_{k + 1,j}\},\min \limits_{i \le k < j \\ s[i,k]是s[i,j]的循环节} \{f_{i,k} + LEN(L) + 2\}) \]

其中\(LEN(x)\)\(x\)的位数,\(L\)为循环节数。

时间复杂度\(O(n ^ 4)\)?显然不是。

\(s[i,k]\)的长度不整除\(s[i,j]\)的长度时,显然不成立,剪枝。

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 110;
char s[N];
int f[N][N];
int n;

int check(int l, int r, int R)
{
    int k = l;
    int cnt = 0;
    for (int i = l; i <= R; i++)
    {
        if (k > r)
            k = l, cnt++;
        if (s[k] != s[i])
            return 0;
        ++k;
    }
    return cnt;
}

int Getlen(int x)
{
    int cnt = 0;
    while (x)
    {
        cnt++;
        x /= 10;
    }
    return cnt;
}

int main(void)
{
    scanf("%s", s + 1);
    n = strlen(s + 1);
    // printf("%d\n", n);
    // for (int i = 1; i <= n; i++)
    // {
    //     cout << s[i];
    // }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            f[i][j] = (i == j) ? 1 : inf;
        }
    }
    // printf("n = %d\n", n);
    for (int len = 2; len <= n; len++)
    {
        for (int i = 1, j = i + len - 1; i <= n - len + 1; i++, j++)
        {
            // printf("i = %d,j = %d,%d\n", i, j, i <= n);
            for (int k = i; k < j; k++)
            {
                f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]);
            }
            for (int k = i; k < j; k++)
            {
                int L = (k - i + 1);
                if (len % L != 0)
                    continue;
                int ck = check(i, k, j);
                // printf("check(%d,%d,%d) = %d\n", i, k, j, ck);
                if (ck > 0)
                {
                    f[i][j] = min(f[i][j], f[i][k] + Getlen(len / L) + 2);
                }
            }
        }
    }
    printf("%d\n", f[1][n]);
    return 0;
}
posted @ 2020-10-03 15:03  luyiming123  阅读(189)  评论(1编辑  收藏  举报