题意:
给定一个字符串,每次可以删除连续的相同的字符。问需要多少次能删除全部的字符。
思路:
区间DP。
dp[l][r] 表示l~r区间所需要的最小次数。
对于每一段区间,考虑两种情况。
- 左右端点相同,那么删除右端点的时候会将左端点一并删除,所以就可以相当于不考虑左端点,就可以由dp[l+1][r],或者dp[l][r-1]转移。
这种情况最优解有可能是分不成左右两个子问题的,可能每次都在区间内删除,然后最后将两端一并删除。 - 如果不相同的话,左右端点一定是在两个不同的操作中被删去。这样的话我们就可以枚举断点,一定分为两部分删去这个区间,取一下最小值就行了。
代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=510;
const int inf=0x3f3f3f3f;
int n,dp[maxn][maxn];
char s[maxn];
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++) dp[i][i]=1;
for(int i=1;i+1<=n;i++)
{
if(s[i]==s[i+1]) dp[i][i+1]=1;
else dp[i][i+1]=2;
}
//以上为初始化。
for(int i=3;i<=n;i++)//枚举区间长度。
{
int r;
for(int l=1;l+i-1<=n;l++)//区间左端点
{
r=l+i-1;
dp[l][r]=inf;
if(s[l]==s[r]) dp[l][r]=min(dp[l+1][r],dp[l][r-1]);
else
{
for(int k=l;k<=r;k++)//枚举两个部分的断点。
dp[l][r]=min(dp[l][k]+dp[k+1][r],dp[l][r]);
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}