题意理解

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

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

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

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

输入格式

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

第二行包含N个整数表示\(g_1\)~\(g_N\)。

输出格式

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

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

数据范围

\[1 \le N \le 30, \\\\
N \le M \le 5000, \\\\
1 \le g_i \le 10^7 \\\\
\]

输入样例:

3 20
1 2 3

输出样例:

2
2 9 9

解题报告

题意理解

\(M\)个饼干,分给\(N\)个孩子,每一个孩子有一个贪婪度\(g[i]\),和嫉妒值\(a[i]\),如果有\(a[i]\)个孩子的饼干数比\(i\)多,那么就会产生怨气\(g[i] \times a[i]\)

要求怨气最少.

思路解析

状态初步

已经获得饼干的孩子个数,和已经发放的饼干,应该是我们的动态规划阶段.

但是我们发现,一个孩子产生的怨气,必然和其他孩子得到的饼干数有关.

\[s[i]=
\begin{cases}
\mathcal a[i] \times g[i] \quad a[i]>0\\\\
\mathcal 0 \quad a[i]=0 \\\\
\end{cases}
\\\\
s[i]表示为第i个孩子他产生的怨气
\]


贪心优化

我们发现动态规划的状态设计,想出来不是很难.

但即使我们发现知道了这个状态设计,我们却无法推出动态规划转移方程.

孩子们的怨气产生是根据情况变化的,我们永远猜测不出来.妹子的心就是这样的

我们不得不想点办法?

其实我们发现,那些贪婪度度大的孩子,应该获得到更多的饼干.

所以说,我们按照贪婪度从小到大排序,然后每个孩子获得的饼干数应该也按照贪婪度从大到小排序.

越贪婪的人,得到的饼干越多.越漂亮的妹子,追求者越多

所以说饼干个数,是单调递减的.


转移方程

我们可以设置一下状态的具体表示.

\[f[i][j]表示为前i个孩子,一共分配了j个饼干的最小怨气和.
\]

那么状态转移一下.

\[a[i+1]=
\begin{cases}
\mathcal a[i+1]=i \quad cnt[i+1]<cnt[i] \quad 前i个孩子饼干都比他多,饼干数是单调递减的\\\\
\mathcal a[i+1] \quad cnt[i+1]=cnt[i] \quad 难以确定,不知道前面有多少个孩子饼干比他多\\\\
\end{cases}
\\\\
cnt[i]表示第i个孩子得到的饼干数
\]

当前问题就是,让我们去处理未知情况,也就是前面到底有多少个孩子饼干比它多.


等价变换
  1. 假如说我们当前第\(i\)个孩子,手中的饼干数大于\(1\)的话.

根据饼干数是单调递减可以得出,

\[我们前i个孩子手中的饼干都是 \ge 1 \\\\
因为第i个孩子得到的饼干个数是最少的 \\\\
一个数列中最小值大于1,那么必然其他值都会大于1. \\\\
\]

分配\(j\)个饼干给前\(i\)个孩子,其实等价于分给\(j-i\)个饼干给前\(i\)个孩子.

相当于每一个孩子都少拿一个.

之所以\(a[i]\)没有改变,是因为所有的孩子们,比他们饼干数量多的孩子个数没有改变,相对的逻辑关系木有变化.

  1. 假如说第i个孩子只有1个饼干

我们只能选择枚举有多少个孩子只有一个饼干了,肯定不会很多.

\[f[i][j]=min
\begin{cases}
\mathcal f[i][j-i] \\\\
\mathcal min_{0 \le k < i}{F[k,j-(i-k)]+k*\sum_{p=k+1}^{i}{g[p]}} \\\\
\end{cases}
\\\\
\]


代码解析

#include <bits/stdc++.h>
using namespace std;
#define fir(i,a,b) for(int i=a;i<=b;i++)
#define int long long
const int N=110;
const int M=5100;
int n,m,i,j,k,f[N][M],s[N],g[N],a[N][M],b[N][M],ans[M],c[N];
int cmp(int a,int b)
{
return g[a]>g[b];
}
void print(int n, int m)
{
if (n==0)
return;
print(a[n][m],b[n][m]);
if (a[n][m] == n)
{
fir(i,1,n)
ans[c[i]]++;
}
else
fir(i,a[n][m]+1,n)
ans[c[i]] = 1;
}
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
fir(i,1,n)
cin>>g[i],c[i]=i;
sort(c+1,c+1+n,cmp);
memset(f,0x3f,sizeof(f));
f[0][0]=0;
fir(i,1,n)
{
s[i]=s[i-1]+g[c[i]];
fir(j,i,m)
{
f[i][j]=f[i][j-i];
a[i][j]=i;
b[i][j]=j-i;
fir(k,0,i-1)
{
if (f[i][j]>f[k][j-(i-k)]+(s[i]-s[k+1-1])*k)
{
f[i][j]=f[k][j-(i-k)]+(s[i]-s[k+1-1])*k;
a[i][j]=k;
b[i][j]=j-(i-k);
}
}
}
}
cout<<f[n][m]<<endl;
print(n,m);
fir(i,1,n)
cout<<ans[i]<<' ';
cout<<endl;
return 0;
}

CH5105 Cookies饼干(线性DP)的更多相关文章

  1. CH5105 Cookies (线性dp)

    传送门 解题思路: 贪心的想,贪婪值越大的孩子应该分得更多的饼干,那么先sort一遍在此基础上进行dp.最直观的方向,可以设dp[i][j]为前i个孩子一共分得j块饼干的怨恨最小值.然后转移第i+1个 ...

  2. CH 5105 Cookies(贪心+DP)

    \(CH 5105 Cookies\) \(solution:\) 真是好题一道!这道题我想了很久很久,就得这一题可以直接完全贪心,可惜最后还是失败了,但是对贪心的深入思考也换来了一个最优解方案.然后 ...

  3. LightOJ1044 Palindrome Partitioning(区间DP+线性DP)

    问题问的是最少可以把一个字符串分成几段,使每段都是回文串. 一开始想直接区间DP,dp[i][j]表示子串[i,j]的答案,不过字符串长度1000,100W个状态,一个状态从多个状态转移来的,转移的时 ...

  4. Codeforces 176B (线性DP+字符串)

    题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28214 题目大意:源串有如下变形:每次将串切为两半,位置颠倒形成 ...

  5. hdu1712 线性dp

    //Accepted 400 KB 109 ms //dp线性 //dp[i][j]=max(dp[i-1][k]+a[i][j-k]) //在前i门课上花j天得到的最大分数,等于max(在前i-1门 ...

  6. 动态规划——线性dp

    我们在解决一些线性区间上的最优化问题的时候,往往也能够利用到动态规划的思想,这种问题可以叫做线性dp.在这篇文章中,我们将讨论有关线性dp的一些问题. 在有关线性dp问题中,有着几个比较经典而基础的模 ...

  7. POJ 2479-Maximum sum(线性dp)

    Maximum sum Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 33918   Accepted: 10504 Des ...

  8. poj 1050 To the Max(线性dp)

    题目链接:http://poj.org/problem?id=1050 思路分析: 该题目为经典的最大子矩阵和问题,属于线性dp问题:最大子矩阵为最大连续子段和的推广情况,最大连续子段和为一维问题,而 ...

  9. nyoj44 子串和 线性DP

    线性DP经典题. dp[i]表示以i为结尾最大连续和,状态转移方程dp[i] = max (a[i] , dp[i - 1] + a[i]) AC代码: #include<cstdio> ...

随机推荐

  1. Spring boot Gradle项目搭建

    Spring boot Gradle项目搭建 使用IDEA创建Gradle工程     操作大致为:File->new->Project->Gradle(在左侧选项栏中)     创 ...

  2. kubeadm安装集群系列(kubeadm 1.15.1)

    kubeadm已经进入GA阶段,所以尝试使用kubeadm从零开始安装高可用的Kubernetes集群,并记录下过程和所有坑 本文基于kubeadm 1.15.1 目录 kubeadm安装集群系列-1 ...

  3. Pandas导入导出&pickle文件模块

    Pandas可以读取与存储的文件格式有很多 像csv,excel,json,html等,详细请看官方文档https://pandas.pydata.org/pandas-docs/stable/use ...

  4. Go语言中方法和函数的区别

    今天看<Go语言实战>发现方法和函数不太一样,写的格式不一样,用法也不一样.所以记一次笔记. 在Go语言中,函数和方法不太一样,有明确的概念区分.其他语言中,比如Java,一般来说,函数就 ...

  5. c++ static_cast和dynamic_cast详解

    注:从图中可以看出,派生类不仅有自己的方法和属性,同时它还包括从父类继承来的方法和属性.当我们从派生类向基类转换时,不管用传统的c语言还是c++转换方式都可以百分百转换成功.但是可怕是向下转换类型,也 ...

  6. Android性能专项分类

    性能专项分类:1.资源消耗2.启动耗时3.主要页面加载时间4.内存泄漏.抖动5.卡顿.页面渲染 一.资源消耗:CPU.内存.流量.功耗-----1.查看CPU占用率:adb shell dumpsys ...

  7. PYTHON 100days学习笔记004:循环结构

    目录 Day04 - 循环结构 1. 循环结构的应用场景 2.for-in循环 3. while循环 4. 练习 4.1 输入一个数判断是不是素数. 4.2 输入两个正整数,计算最大公约数和最小公倍数 ...

  8. [转帖]Docker学习之Dockerfile命令详解

    Docker学习之Dockerfile命令详解 https://it.baiked.com/system/docker/2436.html 图挺好的 前言 之前,制作镜像的伪姿势搭建已经见过了,今天介 ...

  9. python调用hanlp进行命名实体识别

    本文分享自 6丁一的猫 的博客,主要是python调用hanlp进行命名实体识别的方法介绍.以下为分享的全文. 1.python与jdk版本位数一致 2.pip install jpype1(pyth ...

  10. MQTT安全篇

    物联网的核心是连接万物,通过交换并分析数据使得生活更舒适与便捷.不过,敏感数据泄露或者设备被非法控制可不是闹着玩的.比如前段时间国内某著名家电企业的智能洗衣机,使用了某著名电商基于XMPP协议的物联网 ...