[abc265F] Erase Subarrays 题解
空心蓝好简单。为什么我还是打不上蓝呢。
为什么我次次 debug 40+min 呢 /ll
题意:给定数组 \(a\),对每个 \(i \in [1,m]\) 求最少做几次操作让剩余元素和为 \(i\)。
一次操作定义为:取出一个连续子区间并删去。
首先区间不会重叠。如果重叠了可以只用一次删掉。
然后这很一眼 dp。我们来想想为了得到答案,什么是后续转移中需要关心的。
嗯留下来的数的总和需要知道。这一维是 \(O(m)\) 的,存的下。
接着是段数怎么用最少的信息转移。把贡献放到被删除的第一个数上,我们只需要知道一个数和其上个数状态是否相同就能转移。那么再多 \(O(1)\) 大小的一维表示是否被选的状态。
一个数只有选或不选,所以单个状态的转移是 \(O(1)\) 的。
所以就结束了。空间复杂度 \(O(nm)\),时间复杂度 \(O(nm)\),当然你可以用滚动数组把空间做到 \(O(m)\),但显然没必要。
这才能叫给 "beginner" 的 F 罢。
代码很好写也很好调。我的代码中 \(1\) 是这个数不删除,\(0\) 是被删除。
dp 题的重点应该是状态的设计。只要想着什么会影响答案,或者说,怎么用最少的信息量提供能用于转移的信息,就够了。
还有一个思考方向是什么样的情况可以一起转移。感觉这种经典线性 dp 小拓展很板啊。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int M = 3005;
int dp[M][M][2]; int n, a[M], m;
int main(){
scanf("%d %d", &n, &m);
memset(dp, 0x3f, sizeof(dp));
dp[0][0][1] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
for(int j = 0; j <= m; j++) {
dp[i][j][0] = min(dp[i][j][0], dp[i-1][j][1] + 1);
dp[i][j][0] = min(dp[i][j][0], dp[i-1][j][0]);
}
for(int j = 0; j <= m-a[i]; j++) {
dp[i][j+a[i]][1] = min(dp[i][j+a[i]][1], dp[i-1][j][1]);
dp[i][j+a[i]][1] = min(dp[i][j+a[i]][1], dp[i-1][j][0]);
}
}
for(int i = 1; i <= m; i++) {
int ans = min(dp[n][i][0], dp[n][i][1]);
printf("%d\n", ans == 0x3f3f3f3f ? -1 : ans);
}
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/16848951.html

浙公网安备 33010602011771号