题目
题目描述
圣诞老人共有M个饼干,准备全部分给N个孩子。
每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。
如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]*a[i]的怨气。
给定N、M和序列g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。
输入格式
第一行包含两个整数N,M。
第二行包含N个整数表示。
输出格式
第一行一个整数表示最小怨气总和。
第二行N个空格隔开的整数表示每个孩子分到的饼干数,若有多种方案,输出任意一种均可。
数据范围
输入样例:
3 20
1 2 3
输出样例:
2
2 9 9
思路
dp,设 为前
个孩子分
块饼干的最小怨气值。
然后把孩子按贪婪度排序。
转移:
1.若第 个孩子获得的饼干
,就是分配
个饼干给前
个孩子,每人少拿
块饼干;
2.若第 个孩子获得的饼干数
,则枚举
前面的孩子,获得
块饼干。
转移方程:
后面的这一部分可以用前缀和来求。
代码
#include <bits/stdc++.h>
using namespace std;
const int M = 6000, N = 50;
int a[M], num[M], dp[N][M], c[N][M], b[N][M], sum[M], ans[M];
inline bool cmp(int x, int y) {
return a[x] > a[y];
}
void sol(int n, int m) {
if(n == 0) return;
sol(c[n][m], b[n][m]);
if (c[n][m] == n) for (int i = 1; i <= n; i++) ans[num[i]]++;
else for (int i = c[n][m]+1; i <= n; i++) ans[num[i]] = 1;
return ;
}
int main() {
int m, n; scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), num[i] = i;
sort(num + 1, num + n + 1, cmp);
for(int i = 1; i <= n; i++)
sum[i] = sum[i-1] + a[num[i]];
memset(dp, 1292371547, sizeof(dp));
dp[0][0] = 0;
for(int i = 1; i <= n; i++)
for(int j = i; j <= m; j++) {
dp[i][j] = dp[i][j-i];
c[i][j] = i; b[i][j] = j-i;
for(int k = 0; k < i; k++) {
if(dp[i][j] > dp[k][j-(i-k)]+k*(sum[i]-sum[k])) c[i][j] = k, b[i][j] = j-(i-k);
dp[i][j] = min(dp[i][j], dp[k][j-(i-k)]+k*(sum[i]-sum[k]));
}
}
printf("%d\n", dp[n][m]);
sol(n, m);
for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
return 0;
}