查看原题请戳这里
题目大意:
从一个序列中每次取出一个回文串,剩余部分拼起来形成一个新的序列,问取几次能把这个序列取完。
这道题我们可以用区间DP的方法去解决。
若a[1,n]为题目给出的序列,用f[i][j]表示将区间[i,j]中的数取完需要的最少的次数,那么显然f[i][i]=1,且当a[i]=a[j]时f[i][i+1]=1,当a[i]≠a[i+1]时f[i][i+1]=2。有用我们再后续进行动态规划时更新的最短的区间长度为3,所以f[i][i]和f[i][i+1]两种情况需要特殊处理。
在更新f[i][j]时,最简单的思路就是把f[i][j]分成f[i][k]和f[k+1][j]两部分进行处理(其中k∈[i,j)),即
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]),k∈[i,j)
而对于a[i]=a[j]的情况,当区间[i+1,j−1]还剩最后一个可以一次取出的序列b时(显然b是回文的),我们可以将a[i]、a[j]和b组成一个新的回文串同时取出(即组成a[i] b a[j]),从而我们可知当a[i]=a[j]时,
f[i][j]=min(f[i][j],f[i+1][j−1])
然后根据上述两式DP即可。
code:
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #define ll long long #define INF 1000000009 #define qwq printf("qwq\n");
using namespace std;
int read() { register int x = 0,f = 1;register char ch; ch = getchar(); while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();} while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();} return x * f; }
int n,a[505],f[505][505];
int main() { n = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) f[i][j] = INF; for(int i = 1; i <= n; i++) f[i][i] = 1, f[i][i + 1] = 1; for(int i = 1; i < n; i++) if(a[i] != a[i + 1]) f[i][i + 1]++; for(int i = 3; i <= n; i++) for(int j = 1; j <= n - i + 1; j++) { int l = j, r = j + i - 1; if(a[l] == a[r]) f[l][r] = f[l + 1][r - 1]; for(int k = l; k < r; k++) f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]); } printf("%d\n", f[1][n]); return 0; }
|