序列dp

题目大意

个元素,第 个元素是 。一个合法的块的长度必须是块的第一个元素 的值,而你可以删除若干个元素。问你最少需要删除多少个元素才能使得这 个元素可以分成若干个块的组合。

思路

看到题目,可能会想到贪心?二分?但是观察样例,如果是贪心的话我们并不能使用一种每次都必定最优的算法,而我们也不能高效地判断一个解是否合法。那么,压力就来到了 dp 这边。

正着操作显然非常麻烦,那么我们就反向操作。我们设 表示为把 当作第一个块的第一个元素,最少需要更改多少次。那么对于元素 ,我们可以选择舍去或是保留。舍去就很简单了,直接保留 的值并 ;如果保留,那么当前块的长度就是 ,块后面的第一个元素就是,。因此状态转移方程就是:

代码

#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;
}