P2470 [SCOI2007]压缩(原题传送门)
分析:两种情况,有M压缩,无M压缩
f[i][j][0]表示表示i到j的区间内没有M的情况
f[i][j][1]则表示i到j的区间内有M的情况
check(),如果前半段与后半段相等
mid为区间中点
f[i][j][0]=min(f[i][j][0],f[l][mid][0]+1)
然后像平常的区间dp一样,枚举中间点k,并进行更新
f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k)
dp[i][j][1]的情况,中间点k枚举的是‘M’的位置,加上M,前后区间就可以分别压缩了,so…
#include <bits/stdc++.h>
using namespace std;
const int N=105;
char s[N];
int f[N][N][5];
//f[i][j][0]:区间内没有M,f[i][j][1]:区间内有M
int check(int l,int r)
{
//对折并检查
if((l+r)%2==0) return 0;
int md=(l+r)/2;
for(int i=l;i<=md;i++)
{
if(s[i]!=s[i+md-l+1]) return 0;
}
return 1;
}
int main()
{
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
f[i][j][0]=f[i][j][1]=j-i+1;//赋初值
}
}
for(int p=1;p<n;p++)//区间DP
{
for(int i=1;i+p<=n;i++)
{
int j=i+p;
if(check(i,j)) f[i][j][0]=min(f[i][j][0],f[i][(i+j-1)/2][0]+1);
//是否可以压缩
for(int k=i;k<j;k++)
f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k);
//更新
for(int k=i;k<j;k++)
f[i][j][1]=min(f[i][j][1],min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1);
//以第k个为边界,加上一个'M',将区间分为两部分
}
}
cout<<min(f[1][n][0],f[1][n][1])<<endl;;
//cout<<n<<' '<<f[1][n][0]<<' '<<f[1][n][1]<<endl;
return 0;
}