太久没有搞信息了果然会RP--。
考场竟然没有看出来是区间dp,胡乱打了个贪心,好在其他题问题不大。
子状态为(dp[i][j])表示消除区间([i,j])内所有字母所需的最小步数。
我们从两个方向考虑转移。
-
与上一个状态相比,新的字母不能缩短步数,那么直接加1。
-
可以缩短步数。那么枚举与哪一个字母相同。
第一种转移为(dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1)。
第二种转移枚举([i,j])中的(k),然后有(dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]))。
在维护上有一个需要注意的细节:因为第一种转移需要(dp[i+1][j])和(dp[i][j-1])的数据,所以我们需要先枚举(j)再枚举(i),同时(j)从大到小枚举。
AC代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
namespace StandardIO {
template<typename T>inline void read (T &x) {
x=0;T f=1;char c=getchar();
for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>inline void write (T x) {
if (x<0) putchar('-'),x*=-1;
if (x>=10) write(x/10);
putchar(x%10+'0');
}
}
using namespace StandardIO;
namespace Fate {
const int N=1010;
int n;
char c[N];
int dp[N][N]; // min steps for clearing [i,j]
inline void Grand_Order () {
read(n);
for (register int i=1; i<=n; ++i) {
cin>>c[i];
dp[i][i]=1;
}
for (register int r=2; r<=n; ++r) {
for (register int l=r-1; l>=1; --l) {
dp[l][r]=min(dp[l+1][r],dp[l][r-1])+1;
for (register int k=l; k<=r; ++k) {
if (c[l]==c[k]) {
dp[l][r]=min(dp[l][r],dp[l+1][k-1]+dp[k][r]);
}
}
}
}
write(dp[1][n]);
}
}
int main () {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
Fei_De::Gan_Bu_Guo_Ou_De();
}