起因

我满怀欢喜的打开洛谷,发现洛谷 USACO 的绿色DP只剩下了这道!于是粗略扫了一眼,马上编码,炸了!好吧看错了题,事情又变得疏忽迷离……

题面

题目传送门

题目描述

传说,数千年前圣帕特里克消灭了哞尔兰所有的蛇。然而,蛇们现在卷土重来了!圣帕特里克节是在每年的3月17日,所以Bessie要用彻底清除哞尔兰所有的蛇来纪念圣帕特里克。

Bessie装备了一个捕网,用来捕捉 $ N $ 组排成一行的蛇( $ 1 \leq N \leq 400 $ )。Bessie 必须按照这些组在这一行中出现的顺序捕捉每一组的所有蛇。每当 Bessie 抓完一组蛇之后,她就会将蛇放在笼子里,然后带着空的捕网开始捕捉下一组。

一个大小为 $ s $ 的捕网意味着 Bessie 可以抓住任意包含 $ g $ 条的一组蛇,其中 $ g \leq s $ 。然而,每当 Bessie 用大小为 $ s $ 的捕网抓住了一组 $ g $ 条蛇,就意味着浪费了 $ s-g $ 的空间。Bessie 可以任意设定捕网的初始大小,并且她可以改变 $ K $ 次捕网大小( $ 1 \leq K<N $ )。

请告诉 Bessie 她捕捉完所有组的蛇之后可以达到的总浪费空间的最小值。

输入格式

输入的第一行包含 $ N $ 和 $ K $ 。

第二行包含 $ N $ 个整数 $ a_1, \ldots ,a_N $ ,其中 $ a_i $ ( $ 0 \leq a_i \leq 10^6 $ )为第 $ i $ 组蛇的数量。

输出格式

输出一个整数,为 Bessie 抓住所有蛇的总浪费空间的最小值。

样例 #1

样例输入 #1

1
2
6 2
7 9 8 2 3 2

样例输出 #1

1
3

提示

Bessie 可以设置她的捕网开始时大小为 $7$。当她抓完第一组蛇之后,她将她的捕网的大小调整为 $9$,保持这个大小直到抓完第 $3$ 组蛇,再将捕网大小调整为 $3$。总浪费空间为 $ (7-7)+(9-9)+(9-8)+(3-2)+(3-3)+(3-2)=3 $ 。

思路

好吧估计就我看错了,我一开始以为不需要按顺序来这,然后光荣趋势。然后发现问题以后重新推就遇到了困难。去看了题解区,发现是一种叫做 分段DP 的玩意。没听过但是大概了解是下面这样的。

我们注意到他一定是用了一段一段的网然后调整,那么就把答案分成了若干个小段。为了抓住小段里所有的蛇,网肯定是刚刚好等于这个小段里的最大值不会浪费,于是每一段的价值就可以这样表示:

1
2
3
4
5
6
7
8
9
inline void Init() {
for(int i = 1; i <= n; i++) {
int tot = 0, mx = 0;
for(int j = i; j <= n; j++) {
tot += a[j], mx = max(mx, a[j]);
sum[i][j] = mx * (j - i + 1) - tot;
}
}
}

然后就简单了,其实不过如此。但是这种新颖的方式让我眼前一亮,我相信一定会有算法基于这种思想拓展而到的。

然后我们就是枚举一个段数,枚举一个终结点,枚举一个端点,这就更简单了,直接上代码罢。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
// typedef __int128 int128;
typedef pair<int , int > pii;
typedef unsigned long long ull;

namespace FastIO
{
// char buf[1 << 20], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++)
template<typename T> inline T read(T& x) {
x = 0;
int f = 1;
char ch;
while (!isdigit(ch = getchar())) if (ch == '-') f = -1;
while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
x *= f;
return x;
}
template<typename T, typename... Args> inline void read(T& x, Args &...x_) {
read(x);
read(x_...);
return;
}
inline ll read() {
ll x;
read(x);
return x;
}
};
using namespace FastIO;

const int N = 410;

int n, K;
int a[N];

inline void Input() {
read(n, K); K++;
for(int i = 1; i <= n; i++) {
read(a[i]);
}
}

int sum[N][N];

inline void Init() {
for(int i = 1; i <= n; i++) {
int tot = 0, mx = 0;
for(int j = i; j <= n; j++) {
tot += a[j], mx = max(mx, a[j]);
sum[i][j] = mx * (j - i + 1) - tot;
}
}
}

int dp[N][N];

inline void Work() {
Init();
for(int i = 0; i <= n; i++) {
for(int j = 0; j <= K; j++) {
dp[i][j] = 1e9;
}
}
dp[0][0] = 0;
for(int k = 1; k <= K; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 0; j < i; j++) {
dp[i][k] = min(dp[i][k], dp[j][k - 1] + sum[j + 1][i]);
}
}
}
printf("%lld\n", dp[n][K]);
}

int main() {
int T = 1;
while(T--) {
Input();
Work();
}
return 0;
}