洛谷 P4302 [SCOI2003]字符串折叠 题解
每日一题 day68 打卡
Analysis
这道题很容易想到区间DP,但是在DP中还需要判断循环节的压缩这种情况。
另外还有一点,缩写并不一定比之前的字符串优。
eg. aaa -> 3(a)
于是我们开始考虑如何处理压缩的情况。
因为我们转移时是将两个子区间的答案合成到一个大区间内,即 dp[i][k] 和 dp[k+1][j] 推出 dp[i][j]
所以我们不妨设 dp[i][k] 所表示的区间为循环节,如果判断该循环节合法,那么压缩后的答案即为循环节的答案 + 2 + 数字位数。
于是我们得出 dp 方程:
dp[ i ][ j ] = min( dp[ i ][ j ] , dp[ i ][ k ] + dp[ k + 1 ][ j ] ); //正常更新答案
if (循环节合法) dp[ i ][ j ] = min( dp[ i ][ j ] , dp[ i ][ k ] + 2 + digit[ len / cyc ] ); //len为枚举的总区间长度,cyc为循环节长度
那么现在唯一的问题就是如何判断循环节合法
以下为限制条件:
1.如果枚举的总区间长度可以整除循环节的长度
2.判断 dp[ k + 1 ][ j ] 是否满足循环节 (这里的实现基本是暴力,看代码,很好懂)
这样这道题就完成了
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 #define maxn 100+10 7 #define rep(i,s,e) for(register int i=s;i<=e;++i) 8 #define dwn(i,s,e) for(register int i=s;i>=e;--i) 9 using namespace std; 10 inline int read() 11 { 12 int x=0,f=1; 13 char c=getchar(); 14 while(c<'0'||c>'9') {if(c=='-') x=-x; c=getchar();} 15 while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();} 16 return f*x; 17 } 18 inline void write(int x) 19 { 20 if(x<0){putchar('-');x=-x;} 21 if(x>9)write(x/10); 22 putchar(x%10+'0'); 23 } 24 int n; 25 int digit[maxn]; 26 int dp[maxn][maxn]; 27 char a[maxn]; 28 inline bool check(int l,int r,int len) 29 { 30 for(register int i=l;i<=l+len-1;++i) 31 { 32 char ch=a[i]; 33 for(register int j=i;j<=r;j+=len) 34 { 35 if(a[j]!=ch) return false; 36 } 37 } 38 return true; 39 } 40 signed main() 41 { 42 cin>>a+1; 43 n=strlen(a+1); 44 rep(i,1,9) digit[i]=1; 45 rep(i,10,99) digit[i]=2; 46 digit[100]=3; 47 memset(dp,63,sizeof(dp)); 48 rep(i,1,100) dp[i][i]=1; 49 rep(len,1,n) 50 { 51 for(register int i=1;i+len-1<=n;++i) 52 { 53 int j=i+len-1; 54 rep(k,i,j-1) 55 { 56 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 57 int cyc=k-i+1; 58 if(len%cyc!=0) continue; 59 if(check(i,j,cyc)==true) 60 { 61 dp[i][j]=min(dp[i][j],dp[i][k]+2+digit[len/cyc]); 62 } 63 } 64 } 65 } 66 write(dp[1][n]); 67 return 0; 68 }
如有失误请各位大佬斧正(反正我不认识斧正是什么意思)