#序列dp ## 题目大意 有 $n$ 个元素,第 $i$ 个元素是 $a_i$。一个合法的块的长度必须是块的第一个元素 $+1$ 的值,而你可以删除若干个元素。问你最少需要删除多少个元素才能使得这 $n$ 个元素可以分成若干个块的组合。 ## 思路 看到题目,可能会想到贪心?二分?但是观察样例,如果是贪心的话我们并不能使用一种每次都必定最优的算法,而我们也不能高效地判断一个解是否合法。那么,压力就来到了 dp 这边。 正着操作显然非常麻烦,那么我们就反向操作。我们设 $dp_i$ 表示为把 $i$ 当作第一个块的第一个元素,最少需要更改多少次。那么对于元素 $a_i$,我们可以选择舍去或是保留。舍去就很简单了,直接保留 $dp_{(i+1)}$ 的值并 $+1$;如果保留,那么当前块的长度就是 $a_i$,块后面的第一个元素就是,$dp_{(i+a_i+1)}$。因此状态转移方程就是: $ dp_i=\min(dp_{(i+1)}+1,dp_{(i+a_i+1)}) $ ## 代码 ```cpp #include <iostream> using namespace std; const int kMaxN = 2e5 + 5; int a[kMaxN], dp[kMaxN], t, n; int main() { for (cin >> t; t; t--) { cin >> n, dp[n + 1] = 0; for (int i = 1; i <= n; i++) { cin >> a[i]; } dp[n] = a[n] != 0; for (int i = n - 1; i >= 1; i--) { if (i + a[i] <= n) { dp[i] = min(dp[i + 1] + 1, dp[i + a[i] + 1]); } else { dp[i] = dp[i + 1] + 1; } } cout << dp[1] << '\n'; } return 0; } ```