zoukankan      html  css  js  c++  java
  • Cookies

    恩这题在以前的DP专题里也说过,同样没打代码

    圣诞老人共用有个饼干,准备全部分给N个孩子。每个孩子有一个贪婪度,第i个孩子的贪婪度为g[i]。如果有a[i]个孩子拿到的饼干数比第i个孩子多,那么第i个孩子会产生g[i]*a[i]的怨气。给定N,M和序列g,圣诞老人请你帮他安排一种分配方式,使得每一个孩子至少分到一块饼干,并且所以孩子的怨气总和最小。1<=N<=30,N<=M<=5000
    每个孩子的怒气值与其他孩子获得的饼干数量相关联
    emmm似乎很难对状态进行划分得到“子结构”,也很难计算
    出每个孩子的怒气值
    仔细(简单?)思考后我们发现,贪婪度大的孩子应该得到更多的饼干,因此首先可以把孩子的贪婪度从大到小,孩子得到饼干的数量将是单调递减的
    设f[i,j]表示前i个孩子一个分配了j块饼干时,怒气值总和的最小值,直观的思考是考虑分配给第i+1个孩子多少饼干,然后进行转移。
    转移时有两种情况:
    1.当前孩子的贪婪度比下一个孩子大,即:第i个孩子的饼干数比第i+1个孩子多,a[i+1]=i;
    2.当前孩子的贪婪度与下一个孩子一样,即:第i个孩子的饼干数与第i+1个孩子一样,那么这时我们还需要知道前i个孩子中有多少个获得饼干数与第i个孩子相同才能求出a[i+1]
    总而言之,无论哪种情况,我们都需要知道第i个孩子获得的饼干数,以及i前面有多少个孩子与i获得的饼干数相同,然而在现有DP状态下,很难高效维护这两个信息,虽然不是没法维护,比如我们添加两个维度去记录,那是那样我们要多大的数组?四维数组,极限一共30个孩子,5000块饼干,30*5000*30^2,135000000,10位数的总空间,显然没法写。
    不扩维呢,我们需要多开数组,似乎并非不可写?我们似乎确实可以做得到,但是我们对于每个孩子获得多少饼干似乎并没办法得到实现,或者实现麻烦(我懒得想了qwq)
    那么我们不妨对状态转移做一个等价交换,
    1.若第i个孩子获得的饼干数大于1,则等价于分配j-i个饼干给前i个孩子,也就是说平均每个孩子都少分一块饼干,获得饼干数的相对大小顺序不变,从而怨气和也不变
    2.若第i个孩子获得的饼干数为1,枚举i前面有多少个孩子也获得1块饼干
    想想为什么能这样,我们在第i个孩子那,用j-i的方式强行使前i个孩子每个人获得的饼干都少了1,那么这样不断-1-1-1-1...下去,肯定会出现到最后只获得了1块的情况,由1状态可知,这样操作下去并不会是结果有任何变化,所以2状态并不会对结果产生影响,这样我们也就能得到状态转移方程
                    i
    f[i,j] = min{f[i,j-i], min{f[k,j-(i-k)] + k * Σg[p]}(0<=k<i)]}
                    p=k+1
                                            i
    对min中嵌套的min,我们先挨个枚举k,f[k,j-(i-1)]表示到第k个孩子,共分了j-(i-k)块饼干。k*Σg[p]则表示假设从第k
                                            p=k+1
    个孩子向后获得的饼干数都相同,总的怨气和,这样一遍后,我们得到加入i之前有和i获得饼干相等的情况,所能得到的最小怨气和,然后再与i之前没有孩子与i获得饼干数相等的情况所需的怨气和,就能得到第i个孩子得到第j个饼干并且怨气和最小结果
    初态:f[0][0],末态:f[n,m]
    分析一下,O(nmk)(0<=k<i),加上n和k的范围极大的小于m,m也仅有5000,可写
    这道题启发我们,有时可以通过额外的算法确定DP计算顺序,有时可以在状态空间中运用等效手段对状态进行缩放。这样一般可以使需要计算的问题得到极大的简化

    在本题中,在DP前对N个孩子执行排序,使他们获得的饼干单调递减,利用相对大小不变性,把第i+1个孩子获得的饼干缩放到1,在考虑i前面有多少个孩子获得的饼干数量相等,使问题得到极大的简化,容易进行维护和转移

    最后在新简单说一下枚举的问题,枚举的k表示的是从k开始到i全部只获得了1块饼干,也就是说从k到i获得的饼干数量相同。为什么这么枚举呢?我们都把怒气值排过序了,自然按照怒气值来就好了

    最后输出方案时,由于本题有spj,只需输出其中一种方案即可

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define uint unsigned int
     4 #define ull unsigned long long
     5 using namespace std;
     6 const int maxm = 5100;
     7 int g[maxm], id[maxm];
     8 int f[31][maxm];
     9 int jc[31][maxm], bi[31][maxm];
    10 int s[maxm], ans[maxm];
    11 int n, m;
    12 
    13 inline int read() {
    14     int x = 0, y = 1;
    15     char ch = getchar();
    16     while(!isdigit(ch)) {
    17         if(ch == '-') y = -1;
    18         ch = getchar();
    19     }
    20     while(isdigit(ch)) {
    21         x = (x << 1) + (x << 3) + ch - '0';
    22         ch = getchar();
    23     }
    24     return x * y;
    25 }
    26 
    27 inline bool cmp(int a, int b) {
    28 return g[a] > g[b];}
    29 
    30 void print(int n, int m) {
    31     if(n == 0) return;
    32     print(jc[n][m], bi[n][m]);
    33     if(jc[n][m] == n) //当前阶段不存在与选择的孩子所发到的饼干数量相同的孩子 
    34         for(int i = 1; i <= n; ++i) ans[id[i]]++;//从第1到当前位置所有孩子全部加上一块饼干 
    35     else for(int i = jc[n][m] + 1; i <= n; ++i) ans[id[i]] = 1;//如果不相同,存在分到饼干相同的孩子
    36     //将这些孩子缩放到1,因为我们逐层退出递归的顺序与DP计算的顺序相同,所以计算方案是模拟DP转移思路计算 
    37 }
    38 
    39 int main() {
    40     freopen("_.in", "r", stdin);
    41     freopen("_.out", "w", stdout);
    42     n = read(), m = read();
    43     for(int i = 1; i <= n; ++i) {
    44         g[i] = read();
    45         id[i] = i;
    46     }
    47     sort(id + 1, id + n + 1, cmp);
    48     for(int i = 1; i <= n; ++i)
    49         s[i] = s[i - 1] + g[id[i]];
    50     memset(f, 0x3f, sizeof(f));
    51     f[0][0] = 0;
    52     for(int i = 1; i <= n; ++i) 
    53         for(int j = i; j <= m; ++j) {
    54             f[i][j] = f[i][j - i];
    55             jc[i][j] = i, bi[i][j] = j - i;
    56             for(int k = 0; k < i; ++k) {//从k到i获得饼干数全为1 
    57                 if(f[i][j] > f[k][j - (i - k)] + k * (s[i] - s[k])) {
    58                     f[i][j] = f[k][j - (i - k)] + k * (s[i] - s[k]);
    59                     jc[i][j] = k, bi[i][j] = j - (i - k);
    60                 }
    61             }
    62         }
    63     printf("%d
    ", f[n][m]);
    64     print(n, m);
    65     for(int i = 1; i <= n; ++i)
    66         printf("%d ", ans[i]);
    67     printf("
    ");
    68     return 0;
    69 }
  • 相关阅读:
    nodejs理解
    jquery练习
    SASS
    css 继承和层叠
    Session cookie
    vue框架里边的ref='xxx' 的用法 在父组件里边操作子组件的变量 有多组单选按钮(遍历生成)每次只能选中一组
    获取不到页面元素的宽度??????
    刷题很重要
    复盘很重要
    jquery里边的ajax请求,如果是发get请求,就不需要写contentType:'application/json;charset=uft-8'。只有post请求才需要写这个属性。
  • 原文地址:https://www.cnblogs.com/ywjblog/p/9744438.html
Copyright © 2011-2022 走看看