題意:給一個長度不超過500的字符串,每次可以消掉字符串中連續相同的一個子串,消去之後剩下的兩個串自動連在一起,問最少消多少次使字符串清空。
題解:區間dp,一看應該使的但是第一把居然寫出來個的。過了樣例交上去果然WA了,原因是沒有考慮最優解是講原串分成若干子串的情況。所以轉移時需要引入斷點k,從而複雜度多一個n。
比如
6
adabcb
就可以卡掉這個會WA的算法(正解在後面)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=504;
int n;
char s[N];
int f[N][N];
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void smin(int &x,int y) {
x=x>y?y:x;
}
int main() {
// freopen("in.txt","r",stdin);
n=read();
scanf("%s",s+1);
memset(f,INF,sizeof(f));
for (register int i=1;i<=n;++i) f[i][i]=1;
for (int len=2;len<=n;++len)
for (int i=1;i+len-1<=n;++i) {
int j=i+len-1;
f[i][j]=min(f[i+1][j]+1,f[i][j-1]+1);
if (s[i]==s[j]||s[i]==s[i+1])
smin(f[i][j],f[i+1][j]);
if (s[i]==s[j]||s[j]==s[j-1])
smin(f[i][j],f[i][j-1]);
}
/* for (int i=1;i<=n;++i) {
for (int j=1;j<=n;++j)
printf("%-2d ",f[i][j]==INF?-1:f[i][j]);
puts("");
}*/
printf("%d\n",f[1][n]);
return 0;
}
正解
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=504;
int n;
char s[N];
int f[N][N];
inline int read() {
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void smin(int &x,int y) {
x=x>y?y:x;
}
int main() {
// freopen("in.txt","r",stdin);
n=read();
scanf("%s",s+1);
memset(f,INF,sizeof(f));
for (register int i=1;i<=n;++i) f[i][i]=1;
for (int len=2;len<=n;++len)
for (int i=1;i+len-1<=n;++i) {
int j=i+len-1;
for (int k=i;k<j;++k)
if (s[i]==s[j]) smin(f[i][j],f[i][k]+f[k+1][j]-1);
else smin(f[i][j],f[i][k]+f[k+1][j]);
}
printf("%d\n",f[1][n]);
return 0;
}