【CH 5105】Cookies(dp)

5 篇文章 0 订阅

题目

题目描述

圣诞老人共有M个饼干,准备全部分给N个孩子。

每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。

如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]*a[i]的怨气。

给定N、M和序列g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。

输入格式

第一行包含两个整数N,M。

第二行包含N个整数表示g_{1}\sim g_{N}

输出格式

第一行一个整数表示最小怨气总和。

第二行N个空格隔开的整数表示每个孩子分到的饼干数,若有多种方案,输出任意一种均可。

数据范围

1\leq N\leq 30
N\leq M\leq 5000
1\leq g_{i}\leq10^{7}

输入样例:

3 20
1 2 3

输出样例: 

2
2 9 9

思路

 dp,设 dp[i][j]为前 \small i 个孩子分 \small j 块饼干的最小怨气值。

然后把孩子按贪婪度排序。

转移:

1.若第 \small i 个孩子获得的饼干 \small > 1,就是分配 \small j-i 个饼干给前 \small i 个孩子,每人少拿 \small 1 块饼干;
2.若第 \small i 个孩子获得的饼干数 \small = 1 ,则枚举 \small i 前面的孩子,获得 \small 1 块饼干。

转移方程:

\large dp[i][j] = min\left\{\begin{matrix} dp[i][j-i] & \\ \underset{0\leq k< i}{min}\left \{ \left.dp[k][j-(i-k)]+k*\sum_{l=k+1}^{i}g[l]\right \} \right. & \end{matrix}\right.

 

后面的这一部分可以用前缀和来求。

代码

#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;
}

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值