zoukankan      html  css  js  c++  java
  • CH5105 Cookies【贪心】【线性dp】

    5105 Cookies 0x50「动态规划」例题

    描述

    圣诞老人共有M个饼干,准备全部分给N个孩子。每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]*a[i]的怨气。给定N、M和序列g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。1≤N≤30, N≤M≤5000, 1<=gi<=10^7。

    输入格式

    第一行两个整数N,M,第二行N个整数g1~gN。

    输出格式

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

    样例输入

    样例输入1
    3 20
    1 2 3
    
    样例输入2
    4 9
    2 1 5 8

    样例输出

    样例输出1
    2
    2 9 9
    
    样例输出2
    7
    ​2 1 3 3

    来源

    ITMO

    题意:给n个小孩分m颗糖,如果有x个小孩的糖比第i个小孩多的话,那么不满意度就会是g[i]*x

    问总不满意度最小的分糖方式

    思路:贪婪度越大的小孩拿到的糖应该要尽量多,因为要使得尽量少的小孩糖数比他多

    所以先按照贪婪度从大到小排个序,他们拿到的糖果数是非递增的

    用“已获得饼干的孩子数”和“已发放的饼干数”作为DP的阶段

    dp[i, j]表示前i个孩子一共分配j块饼干时,怨气总和的最小值

    那么第i+1个小孩有两种情况:1.他拿到的比第i个小孩少,则比他糖果多的有i个人 

    2.他拿到的和第i个小孩一样多,此时需要找到前i个中,和第i个一样多的小孩的个数

    需要用等效的方法

    当我们发现第i个小孩的饼干数大于1时,我们就给前面每个小孩的饼干数减1,也就是说dp[i][j] = dp[i][j - i]。因为每个人都减1,相对的大小是不变的

    当第i个小孩的饼干数等于1时,就枚举i前面有多少小孩也是1块饼干。此时d[i][j] = dp[i][j - (i - k)] + k * g[p](p = k+1~i)。假设有k个小孩不是1.

    求最小的dp[n][m]即可,因为要输出方案,所以每次需要存一下

    a[i][j] = i说明是第一种情况,否则是第二种情况。b数组用来回溯 

    虐狗宝典笔记:

    有时可以通过额外的算法确定DP状态的计算顺序,有时可以在状态空间中运用等效手法对状态进行缩放。

    本题中我们利用贪心,在DP前进行排序,使获得的饼干数单调递减

    还利用相对大小的不变性,把第i+1个孩子获得的饼干数缩放到1,再考虑i前面有几个孩子获得的饼干数量相等

    #include <bits/stdc++.h>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<stdio.h>
    #include<cstring>
    #include<map>
    
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long LL;
    
    int n, m;
    const int maxn = 35, maxm = 5005;
    struct node{
        int g, id;
    }child[maxn];
    int dp[maxn][maxm], sum[maxn];
    int a[maxn][maxm], b[maxn][maxm], ans[maxn];
    
    bool cmp(node a, node b)
    {
        return a.g > b.g;
    }
    
    void print(int n, int m)
    {
        if(n == 0)return ;
        print(a[n][m], b[n][m]);
        if(a[n][m] == n){
            for(int i = 1; i <= n; i++)ans[child[i].id]++;
        }
        else{
            for(int i = a[n][m] + 1; i <= n; i++)ans[child[i].id] = 1;
        }
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++){
            scanf("%d", &child[i].g);
            child[i].id = i;
        }
        memset(dp, inf, sizeof(dp));
        sort(child + 1, child + 1 + n, cmp);
    
        dp[0][0] = 0;
        for(int i = 1; i <= n; i++){
            sum[i] = sum[i - 1] + child[i].g;
            for(int j = i; j <= m; j++){
                int tmp = inf;
                dp[i][j] = dp[i][j - i];
                a[i][j] = i;
                b[i][j] = j - i;
                for(int k = 0; k < i; k++){
                    if(dp[k][j - i + k] + k * (sum[i] - sum[k]) < dp[i][j]){
                        dp[i][j] = dp[k][j - i + k] + k * (sum[i] - sum[k]);
                        a[i][j] = k;
                        b[i][j] = j - i + k;
                    }
                }
            }
        }
        printf("%d
    ", dp[n][m]);
        print(n, m);
        printf("%d", ans[1]);
        for(int i = 2; i <= n; i++){
            printf(" %d", ans[i]);
        }
        printf("
    ");
        return 0;
    }
  • 相关阅读:
    C# 观察者模式(Observer)
    CXGRID设置Selstart和SelLength
    Delphi 中相对路径与绝对路径、系统环境变量等相关函数说明
    用Delphi创建一个空的Access数据库
    非COM环境下的接口编程
    VCLZIP样例
    delphi 文件CRC32校验
    delphi中无类型文件读写
    Delphi调用MSSQL存储过程返回的多个数据集
    CXGRID,定位并高亮
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9745408.html
Copyright © 2011-2022 走看看