$ CH 5105 Cookies $



$ solution: $

真是好题一道!这道题我想了很久很久,就得这一题可以直接完全贪心,可惜最后还是失败了,但是对贪心的深入思考也换来了一个最优解方案。然后这一题的DP也考的很有技术!

贪心1:这一道题当时第一眼就是贪心。首先不管每一个孩子的贪婪度,只要我们将糖果分为 $ N $ 份,实际上就已经确定了会存在多少个孩子他们的糖果数比多少个其他孩子要少。于是我们贪心的让贪婪度小的孩子去做那些糖果数比多少个其他孩子要少的孩子。(虽然这在伦理上说不过去)

贪心2,它可以减少数据范围:如果某些孩子的糖果数一样那么他们彼此之间就不会产生怨气。所以当我们将糖果平均分时,产生的怨气最少(就是没有怨气)。但是我们的糖果数不一定就能平均分了,但是我们依旧可以贪心的让相同糖果数的人尽量多。其次我们可以想到:如果糖果不够平均分,那么一定存在几个人他们的糖果数比其他所有人都低!这样我们就能产生一个很自然的想法:先平均分,如果还剩下几个不够让所有的孩子在多拿一个糖果,就钦定从贪婪度嘴小的孩子里拿回糖果给其他的孩子使得除了这个孩子以外其他孩子都是平均分的(真的好不公平啊),这样肯定能最优。但是我们同样不能保证把这个孩子的所有糖果都拿回能使得(除了这个孩子以外其他孩子都是平均分的)。但是这种情况在 $ m>n^2 $ 的时候可以得到保证,多以我们可以将 $ m $ 限制在 $ n^2 $ 以内,于是数据范围就被我们消减了,然后博主就拿到了CH上的最优解。。。。。。

然后讲一下DP,这道题的DP比较难想,因为我们首先需要想到第一个贪心,然后我们发现贪婪度小的孩子分的糖果少,于是我们按照贪婪度从大到小排序,这就符合我们线性DP的要求了。我完全可以设 $ F[i][j] $ 表示前 $ i $ 个孩子用了 $ j $ 颗糖果。但是这一题DP也难快速转移,我们如果正常转移花费的时间很多,会超时(如果我们用第二个贪心减少数据范围,也可以跑过去)。于是我们需要优化,这时候我们发现由于后面的孩子因为贪婪度小分到的糖果少一些,于是我们可以转移一下:假如第 $ i+1 $ 个孩子比第 $ i $ 个孩子得到的糖果少一个,那么这就相当于前 $ i $ 个孩子都多拿了一个糖果(这两个想法是等效的)。于是转移方程变成了:

$ F[i][j]={{F[i][j-i]}_{{ min}_{0\leq k<i}{F[k][j-(i-k)]+k\times (S[i]-S[k]) }}} $

注: $ S[i] $ 表示前 $ i $ 个孩子的贪婪度总和。



$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set> #define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int using namespace std; int n,m;
int S[35];
int as[35];
int f[35][905]; struct su{
int da,id;
inline bool operator <(const su &x)const{return da>x.da;}
}a[35]; struct pi{
int x,y;
}d[35][905]; inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar())) if(ch=='-')sign=1;
while(isdigit(ch)) res=res*10+(ch^48),ch=getchar();
return sign?-res:res;
} inline void tepan(){
printf("%d\n",a[n].da*(n-1));
rg ans=m/n,res=m%n;
for(rg i=1;i<n;++i) as[a[i].id]=ans+bool(res);
as[a[n].id]=ans; if(res) as[a[n].id]-=n-res-1;
for(rg i=1;i<=n;++i) printf("%d ",as[i]);
puts(""); exit(0);
} inline void print(int i,int j){
if(i==0)return ;
else print(d[i][j].x,d[i][j].y);
if(d[i][j].x==i) for(rg k=1;k<=i;++k)++as[a[k].id];
else for(rg k=d[i][j].x+1;k<=i;++k)++as[a[k].id];
} int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=qr(); m=qr();
for(rg i=1;i<=n;++i)
a[i].da=qr(),a[i].id=i;
sort(a+1,a+n+1); if(m>n*n)tepan();
for(rg i=1;i<=n;++i) S[i]=S[i-1]+a[i].da;
for(rg i=1;i<=m;++i) f[0][i]=1e9;
for(rg i=1;i<=n;++i){
for(rg j=0;j<=m;++j){
if(j<i){f[i][j]=1e9; continue;}
f[i][j]=f[i][j-i]; d[i][j].x=i; d[i][j].y=j-i;
for(rg k=0;k<i;++k){
rg tot=f[k][j-(i-k)]+k*(S[i]-S[k]);
if(tot<f[i][j])f[i][j]=tot,d[i][j].x=k,d[i][j].y=j-(i-k);
}
}
} printf("%d\n",f[n][m]); print(n,m);
for(rg i=1;i<=n;++i) printf("%d ",as[i]);
puts(""); return 0;
}

最新文章

  1. 前端之HTML知识点整理
  2. Hadoop概括——学习笔记&lt;一&gt;
  3. 清除SQL server2008 记住的用户名和密码
  4. Web自定义协议,BS端启动CS端,
  5. 对于C#中的一些点滴你真的理解了吗?
  6. Git服务器搭建及SSH无密码登录设置
  7. Varint编码
  8. 关于如何导入GPUImage
  9. The model used to open the store is incompatible with the one used to create the store
  10. gcd&amp;&amp;lcm
  11. [转载]Nginx 反向代理、负载均衡、页面缓存、URL重写及读写分离详解
  12. linux shell (()) 双括号运算符使用
  13. NewBuiltBottomSheetDialog【新建底部对话框】
  14. Liquibase使用入门
  15. ansible 循环与条件判断when
  16. 机器Coding For WPF
  17. hdu 1540(线段树区间合并)
  18. php file_get_contents读取大容量文件方法
  19. 3-1 vue-resource基础介绍
  20. yii2关联查询两组一对一

热门文章

  1. [BZOJ2393] Cirno的完美算数教室(dfs+容斥原理)
  2. 使用HttpClient实现对第三方服务器的请求并接受返回数据
  3. 洛谷—— P3395 路障
  4. 洛谷——P1057 传球游戏
  5. hadoop学习 的yarn
  6. 12/10 C语言程序设计竞赛 后五题
  7. Ubuntu 16.04安装Mac OS 12虚拟机资源(没成功,但资源还是可以用)
  8. Ubuntu 16.04下快速在当前目录打开终端的快捷键设置
  9. GeoServer发布Heatmap
  10. Qt5官方demo解析集30——Extending QML - Binding Example