【POJ2176】Folding

一道别样的区间dp,重点在于对字符串的处理。

读完题,我们很自然的想到区间dp,定义f[i][j]表示从折叠i~j的部分的最小长度,那么答案为f[1][n]。

区间dp的转移一般而言都是一样的,在区间中枚举一个位置,使这一个大区间分成两个小区间。因此状态转移方程我们不在赘述。

我们重点探讨一下对于字符串的处理,首先我们枚举区间的长度,左端点(右端点可以求出),这是区间dp的基本方法,之后我们枚举折叠成每一段的长度,显然区间的长度一定是每一段长度的正整数倍,之后我们在判断一下把这个区间分成这么多段之后每一段是否相同,如果相同,则表明可以折叠。我们将折叠之后的字符串覆盖元原区间的字符串。具体地,我们用一个结构体表示一个状态,结构体中有两个元素,一个表示字符串的长度,另一个表示字符串。因此算法就这样设计完成了。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 #define inf 1e9
 7 typedef long long ll;
 8 struct node {
 9     int len;
10     char s[110];
11 }f[110][110];
12 int n;
13 char s[110];
14 int main() {
15     scanf("%s",s+1);
16     n=strlen(s+1);
17     for(int i=1;i<=n;i++) {
18         f[i][i].len=1;
19         f[i][i].s[0]=s[i];
20     }
21     for(int l=2;l<=n;l++) {
22         for(int i=1;i+l-1<=n;i++) {
23             int j=i+l-1;
24             f[i][j].len=inf;
25             for(int k=1;k<=l/2;k++) {
26                 if(l%k) continue ;
27                 int x=i,y=i+k;
28                 while(s[x]==s[y]&&y<=j) x++,y++;
29                 if(y>j) {
30                     int num=l/k;
31                     sprintf(f[i][j].s,"%d",num);
32                     strcat(f[i][j].s,"(");
33                     strcat(f[i][j].s,f[i][i+k-1].s);
34                     strcat(f[i][j].s,")");
35                     f[i][j].len=strlen(f[i][j].s);
36                     break ;
37                 }
38             }
39             for(int k=i;k<j;k++) {
40                 if(f[i][j].len>f[i][k].len+f[k+1][j].len) {
41                     f[i][j].len=f[i][k].len+f[k+1][j].len;
42                     strcpy(f[i][j].s,f[i][k].s);
43                     strcat(f[i][j].s,f[k+1][j].s);
44                 }
45             }
46         }
47     }
48     puts(f[1][n].s);
49     return 0;
50 }
AC Code
原文地址:https://www.cnblogs.com/shl-blog/p/10770926.html