题目
题意:
给你一组序列,序列中相邻的两个一样的值可以合并成一个值+1的数,问最后剩下的最少的元素有多少个。
思路:
本来我的思路是用栈或者队列的方式去合并,然后输出元素的个数,但是发现假如1 1 1 2这样的如果用那种方法会变成2 1 2但是最少的应该是1 3所以要用动态规划去求。
这里的
d
p
i
j
dp_{ij}
dpij指的是
i
i
i到
j
j
j如果合并完剩下1个数字的值,所以我们有了这个三重循环去更新这个值,在
i
i
i~
j
j
j中置入一个k将这个区间分成两个,如果这两个区间的dp是相等的,就说明这
d
p
i
j
dp_{ij}
dpij就是这两个值的合并的值也就是等于
d
p
i
k
+
1
dp_{ik}+1
dpik+1.
接着更新答案值了,我们看判断条件,如果
d
p
[
j
+
1
]
[
i
]
dp[j+1][i]
dp[j+1][i]存在的话就说明了
j
+
1
j+1
j+1 ~
i
i
i中有只有一个值,因为剩余的已经合并了,所以我们就要判断
m
i
n
(
a
n
s
i
,
a
n
s
j
+
1
)
min(ans_i, ans_j + 1)
min(ansi,ansj+1)因为从
j
j
j~
i
i
i中
j
+
1
j+1
j+1 ~
i
i
i只有一个,所以我们只要判断最小就行了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <string>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
typedef vector<int> vec;
template <class T>
inline void read(T &ret) {
char c;
int sgn;
if (c = getchar(), c == EOF) return ;
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 ;
}
inline void out(int x) {
if (x > 9) out(x / 10);
putchar(x % 10 + '0');
}
const int maxn = 510;
const int inf = 0x3f3f3f3f;
int dp[maxn][maxn], ans[maxn], a[maxn];
int main() {
int n;
read(n);
for (int i = 1; i <= n; i++) read(a[i]);
for (int i = n; i >= 1; i--) {
dp[i][i] = a[i];
for (int j = i + 1; j <= n; j++) {
dp[i][j] = 0;
for (int k = i; k <= j; k++) {
if (dp[i][k] && dp[i][k] == dp[k + 1][j]) dp[i][j] = dp[i][k] + 1;
}
}
}
for (int i = 1; i <= n; i++) {
ans[i] = inf;
for (int j = 0; j < i; j++) {
if (dp[j + 1][i]) ans[i] = min(ans[i], ans[j] + 1);
}
}
out(ans[n]);
return 0;
}