题意: 在一个一维坐标上,有 n 个东西, 每个东西, 用 xi, si 表示 这个东西在 xi 位置上,
它能覆盖到的区间为 [ xi - si, xi + si ];
然后, 你可以对任意的东西, 扩大它的 覆盖区间, 即对 si 加 1; 花费1;
问你 覆盖 [ 1, m ] 的最少花费。
n <= 80, m <= 100000;
解: 显然 DP;
我们用 dp[ i ] 表示 覆盖 i ~ m 的最少花费。
然后我们从 m ~ 1 枚举 x;
若这个x 被某个东西覆盖, 则 dp[ i ] = dp[ i + 1 ];
否则, 枚举 所有的 n 个东西, 判断 那些 xi - si 大于你当前枚举的那个 x 的东西。
那么你设 dis = xi - si - x; 即你到那个东西覆盖的区间的左端点的距离。
然后, 因为它左边扩了 dis, 那么它右边也可以扩 dis;
那么,答案就是 dis + dp[ xi + si + dis];
然后, 最后 dp[ 1 ] 就是答案了。
#include <bits/stdc++.h> #define LL long long #define INF 0x3f3f3f3f using namespace std; struct note { int l, r; }a[100]; bool cmp(note a, note b) { return a.l == b.l ? a.r < b.r : a.l < b.l; } const int N = 2e5 + 5; int dp[N]; int main() { int n, m; scanf("%d %d", &n, &m); for(int i = 0; i <= m; i++) dp[i] = INF; int ma = 0; for(int i = 1; i <= n; i++) { int x, s; scanf("%d %d", &x, &s); a[i].l = max(x - s, 1); a[i].r = min(x + s, m); ma = max(ma, a[i].r); for(int j = a[i].l; j <= a[i].r; j++) { dp[j] = 0; } } for(int i = m; i > ma; i--) dp[i] = m - i + 1; // n++; a[n].l = n; a[n].r = n; sort(a + 1, a + 1 + n, cmp); dp[m + 1] = 0; for(int i = m; i >= 1; i--) { if(dp[i] == 0) dp[i] = dp[i + 1]; else { for(int j = 1; j <= n; j++) { if(a[j].l > i) { int dis = a[j].l - i; int R = min(a[j].r + dis, m); dp[i] = min(dp[i], dis + dp[R + 1]); } } } } printf("%d ", dp[1]); }