挂机一个多小时都没想出来这道题,不过好在前四题切的快还是上分了
看到有人说这题是区间DP裸题...我怀疑我要回炉再造了
两种解法,一种是O(n^3)的区间DP
先预处理哪些段可以合并成为一个数字,复杂度n^3,再区间DP
f(i, j) = min(f(i, k) + f(k + 1, j)) ( i <= k < j)
#include <cstdio> #include <algorithm> using namespace std; const int N = 510; int a[N], num[N][N], f[N][N]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); num[i][i] = a[i]; } for (int len = 1; len < n; len++) for (int l = 1; l <= n - len; l++) { int r = l + len; f[l][l] = 1; for (int i = l; i < r; i++) if (num[l][i] == num[i + 1][r] && num[l][i] > 0) num[l][r] = num[l][i] + 1; if (num[l][r] > 0) f[l][r] = 1; else f[l][r] = r - l + 1; } f[n][n] = 1; for (int len = 1; len < n; len++) for (int l = 1; l <= n - len; l++) { int r = l + len; for (int i = l; i < r; i++) f[l][r] = min(f[l][r], f[l][i] + f[i + 1][r]); } printf("%d", f[1][n]); return 0; }
但是其实有更美观复杂度更低的做法(学长教我的
用栈把预处理优化到n^2,然后用d[i]表示前i个最少能变成几个,递推式就变成了
d[i] = d[j - 1] + 1 (if i ~ j可以合并成一个)
#include <cstdio> #include <algorithm> #include <stack> using namespace std; const int N = 510; int a[N], d[N]; int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) { stack < int > s; d[i] = d[i - 1] + 1; s.push(a[i]); for (int j = i - 1; j > 0; j--) { if (s.empty() || s.top() != a[j]) s.push(a[j]); else { int u = a[j]; while (!s.empty()) { if (u != s.top()) break; u = s.top() + 1; s.pop(); } if (s.empty()) d[i] = min(d[i], d[j - 1] + 1); s.push(u); } } } printf("%d", d[n]); return 0; }