7 ACwing 277 cookies 题解
cookies
题面
圣诞老人共有 M 个饼干,准备全部分给 N 个孩子。
每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。
如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]×a[i] 的怨气。
给定 N、M 和序列 g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。
\(1 \le N \le 30\)
\(N \le M \le 5000\)
题解
这道题的阶段比较明显,就是“分了几个人”,“分了多少饼干”,所以状态很容易设,但是并不是很好转移,因为每个人的贡献要根据其他人分得的饼干数确定,所以我们需要确定一个分饼干的顺序,方便我们计算每个人的贡献
根据“邻项交换法”容易证明,贪婪度大的孩子拿到更多的饼干是更优的,所以我们将孩子按照贪婪度从大到小排序,这样他们分得的饼干数也是从大到小的,非严格单减
但如果有两个人的饼干数相同,那么后面那个人的贡献就不是 \(g_i \times (i - 1)\) ,而是 \(g_i \times (i - 2)\)
所以我们需要维护有多少个人是相同的,但这个东西在我们 dp 数组中并不好维护,所以我们采用另一种方法,想想正整数拆分的过程,我们将 \(j\) 个饼干分给 \(i\) 个人,其实也就相当于把 \(j\) 这个数拆分成 \(i\) 个正整数
我们可以利用类似正整数拆分的方法,每次要么整体 +1,要么新插入一个 1,这样我们在插入最后一个人的时候,可以枚举前面有几个 1 ,也就是这些人都拥有相同的饼干数
为什么这样可行?因为在考虑一个人的贡献的时候,我们只考虑有多少个人比他多,也就是他们之间的相对关系,我们并不关心每个人具体有多少,所以整体 +1 的操作并不会影响我们的答案
那么设 \(f(i,j)\) 表示前 \(i\) 个人分了 \(j\) 个饼干的最小贡献,转移为
k$ 相当于我们枚举的前面 \(1\) 的个数,\(f(i,j) = f(i,j-i)\) 相当于整体 +1
那个 \(\sum\) 可以用前缀和 \(O(1)\) 算,所以总的时间复杂度为 \(O(N^2M)\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int N = 50, M = 5010;
int n, m;
ll f[N][M], sum[N];
int ans[N];
struct node {
int g, id;
} a[N];
bool cmp (node a, node b) {
return a.g > b.g;
}
int main () {
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i].g;
a[i].id = i;
}
sort (a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
sum[i] = sum[i - 1] + a[i].g;
}
memset (f, 0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j >= i) f[i][j] = f[i][j - i];
for (int k = 1; k <= i && k <= j; k ++) {
f[i][j] = min (f[i][j], f[i - k][j - k] + (i - k) * (sum[i] - sum[i - k]));
}
}
}
cout << f[n][m] << endl;
{
//h 表示前面 +1 的次数
int i = n, j = m, h = 0;
while (i && j) {
if (j >= i && f[i][j] == f[i][j - i]) {
j -= i;
h ++;
} else {
for (int k = 1; k <= i && k <= j; k ++) {
if (f[i][j] == f[i - k][j - k] + (i - k) * (sum[i] - sum[i - k])) {
for (int u = i; u > (i - k); u--) {
ans[a[u].id] = 1 + h;
}
i -= k;
j -= k;
break;
}
}
}
}
}
for (int i = 1; i <= n; i ++) {
cout << ans[i] << ' ';
}
return 0;
}

浙公网安备 33010602011771号