CF1312E Array Shrinking 区间dp
给定一个长度为 \(n\) 的序列 \(a\),\(1 \le n \le 500, 1 \le a_i \le 1000\)
类似 2048 的玩法,每次可以将相邻的两个一样的数字 x 合并为 x + 1,求最后整个序列的最小长度为多少
一个比较显然的结论是本题的数据范围和做法是区间 dp,因为涉及到区间的合并问题
然后会发现直接做区间 dp 的话会很不好写,因为比较显然的结论是一个区间并不能由两个小区间去决定它的答案,可能是由很多个小区间才能合并组成,当区间的答案为 1 时才会去考虑合并还要考虑数字是多少
令 \(f_{i,j}\) 表示 \(i \to f_{i,j}-1\) 这一段可以组成一个数 \(j\),\(f_{i,j}=0\) 表示从 \(i\) 为起点不能构成 \(j\) 这个数
在 \(O(n\cdot max(a_i))\) 的复杂度内就可以解决这个问题
- 首先 \(a_i=j\) 时显然有 \(f_{i,j}=i+1\)
- \(f_{i,j}=f_{f[i][j-1],j-1}\) 就是往后枚举两个 \(j-1\),注意要第一维枚举 \(j\) 顺序问题
然后 \(dp_{i,j}\) 表示这个区间内至少需要多少个数表示,首先置为 \(inf\),对于 \(f_{i,j}\) 存在的点,则把 \(dp_{i,f[i][j] - 1}\) 置为 1 表示这个区间只需要一个数表示,然后做第一维枚举区间长度的区间 \(dp\) 即可,\(dp_{1,n}\) 就是答案
/*================================================================
*
* 创 建 者: badcw
* 创建日期: 2020/3/9
*
================================================================*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 505;
const int maxm = 2005;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
ll res = 1;
while (n > 0) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
template <class T>
inline bool scan(T& ret) {
char c;
int sgn;
if (c = getchar(), c == EOF) return 0; // EOF
while (c != '-' && (c < '0' || c > '9')) c = getchar();
sgn = (c == '-') ? -1 : 1;
ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
ret *= sgn;
return 1;
}
//template <class T>
//inline void out(T x) {
// if (x > 9) out(x / 10);
// putchar(x % 10 + '0');
//}
int n;
int a[maxn];
int f[maxn][maxm];
int dp[maxn][maxn];
int main(int argc, char* argv[]) {
scanf("%d", &n);
memset(dp, 0x3f, sizeof dp);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), dp[i][i] = 1;
for (int j = 1; j < maxm; j++) {
for (int i = 1; i <= n; i++) {
if (a[i] == j) f[i][j] = i + 1;
else f[i][j] = f[f[i][j - 1]][j - 1];
if (f[i][j]) dp[i][f[i][j] - 1] = 1;
}
}
for (int len = 1; len <= n - 1; ++len) {
for (int i = 1; i + len <= n; ++i) {
int j = i + len;
for (int k = i; k < j; ++k) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j]);
}
}
}
int res = min(n, dp[1][n]);
printf("%d\n", res);
return 0;
}