题解:P10971 Cookies
首先考虑贪心邻项交换,设有
其中
在输出方案时是最有趣的地方,解决了我看书做题时不懂的地方(输出方案是书上没有涉及的)。首先根据常规套路,用 pre 数组记录方案的转移,然后递归输出,得到:
0 0
2 2
2 4
3 5
3 8
3 11
3 14
3 17
3 20
啊,这是什么意思?仔细理解一下,发现当
i j a[1~3]
0 0 0 0 0
2 2 1 1 0 // 转移过程: i = 2 时,在 0 0 0 的基础上将最后 2 位改为 1
2 4 2 2 0 // 转移过程: i = 2 时,在 1 1 0 的基础上全部增加 1
3 5 2 2 1 // 转移过程: i = 3 时,在 2 2 0 的基础上将最后 1 位改为 1
3 8 3 3 2 // 转移过程: i = 3 时,在 2 2 1 的基础上全部增加 1
3 11 4 4 3 // 以此类推...
3 14 5 5 4
3 17 6 6 5
3 20 7 7 6
这样就很好理解了转移的过程,递归时修改用前缀和就行。
#include <bits/stdc++.h>
#define LL long long
#define fq(i,d,u) for(int i(d); i <= u; ++i)
#define fr(i,u,d) for(int i(u); i >= d; --i)
using namespace std;
const int N = 35, M = 5e3+5;
struct from {
int i, j;
} pre[N][M];
struct node {
int v, id;
} g[N];
int n, m, f[N][M], s[N], idx[N], ans[N];
// 前 i 个人,花费 j 个饼干的最小怨气
bool cmp(node a, node b) {
return a.v > b.v;
}
void back(from p, from fa) {
if (p.i == fa.i) idx[1]++, idx[p.i + 1]--;
else idx[p.i + 1]++, idx[fa.i + 1]--;
if (p.i == 0) return ;
back(pre[p.i][p.j], p);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
fq(i, 1, n) cin >> g[i].v, g[i].id = i;
sort(g + 1, g + 1 + n, cmp);
fq(i, 1, n) s[i] = s[i - 1] + g[i].v;
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
fq(i, 1, n) fq(j, i, m) {
int ret = f[i][j - i];
pre[i][j] = {i, j - i};
fq(k, 0, i - 1) { // 枚举 k ~ i 都为 1 的 f 最小值
int val = f[k][j - (i - k)] + k * (s[i] - s[k]);
if (val < ret) {
ret = val;
pre[i][j] = {k, j - (i - k)}; // (i - k) 表示 后面 1 的费用
}
}
f[i][j] = min(f[i][j], ret);
}
cout << f[n][m] << '\n';
back(pre[n][m], from {n, m});
fq(i, 0, n) idx[i] += idx[i - 1];
fq(i, 1, n) ans[g[i].id] = idx[i];
fq(i, 1, n) cout << ans[i] << ' ';
return 0;
}