Codeforces Round #787 (Div. 3) G. Sorting Pancakes
Sorting Pancakes
题面翻译
题意简述
有 \(n\) 个箱子和 \(m\) 个小球,初始时第 \(i\) 个箱子有 \(a_i\) 个小球。每次操作可以将一个小球移到相邻的箱子里。求要使得最终数组 \(a_i\ge a_{i+1}\) 的最小操作次数。
输入格式
第一行两个正整数 \(n,m\)。
第二行 \(n\) 个非负整数 \(a_i\)。数据保证 \(\sum a_i=m\)。
输出格式
输出一行一个非负整数,表示答案。
数据规模
\(1\le n,m\le 250\)
样例 #1
样例输入 #1
6 19
5 3 2 3 3 3
样例输出 #1
2
样例 #2
样例输入 #2
3 6
3 2 1
样例输出 #2
0
样例 #3
样例输入 #3
3 6
2 1 3
样例输出 #3
1
样例 #4
样例输入 #4
6 19
3 4 3 3 5 1
样例输出 #4
3
样例 #5
样例输入 #5
10 1
0 0 0 0 0 0 0 0 0 1
样例输出 #5
9
提示
In the first example, you first need to move the pancake from the dish $ 1 $ , after which $ a = [4, 4, 2, 3, 3, 3] $ . After that, you need to move the pancake from the dish $ 2 $ to the dish $ 3 $ and the array will become non-growing $ a = [4, 3, 3, 3, 3, 3] $ .
思路
注意到数据范围都很小,可以使用 \(O(n^3)\) 复杂度的算法通过本题。
首先我们将整个数组翻转过来,考虑将数组变成一个单调不减的数组。
考虑 \(DP\) ,令 f[i][j][k]
表示前 \(i\) 个箱子,一共使用了 \(j\) 个小球,且第 \(i\) 个箱子放了 \(j\) 个小球的最小移动次数,f[i][j][k]
可以转移到 f[i][j + p][p](p >= k)
,从f[i][j][k]
转移到 f[i][j + p][p]
所需要的代价就是 abs(s[i] - j - k)
,但是这么转移是 \(O(n^4)\) 。 考虑优化,由于f[i][j + p][p](p >= k)
\(p \ge k\),因此我们可以在转移的时候维护一个 \(\min(f_{i-1,j,k})(k \le p)\) ,复杂度优化为 \(O(n^3)\) 。
CODE
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 252;
int a[N], s[N], f[N][N][N];
template < typename T >
inline void chkmax(T &x, T y) {x = x >= y ? x : y;}
template < typename T >
inline void chkmin(T &x, T y) {x = x <= y ? x : y;}
int main() {
int n, m; scanf("%d%d", &n, &m);
//f[i][j][k], 第i个箱子有j个球,并且前i个箱子有k个
for (int i = n; i >= 1; i -- ) scanf("%d", a + i);
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];
memset(f, 0x3f, sizeof f);
f[0][0][0] = 0;
for (int i = 1; i <= n; i ++ ) {
for (int j = 0; j <= m; j ++ ) {
int mi = 1 << 30;
for (int k = 0; j + k <= m; k ++ ) {
chkmin(mi, f[i - 1][j][k]);
chkmin(f[i][j + k][k], mi + abs(s[i] - j - k));
}
}
}
printf("%d\n", *min_element(f[n][m], f[n][m] + m + 1));
return 0;
}