http://contest-hunter.org:83/contest/0x50%E3%80%8C%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E3%80%8D%E4%BE%8B%E9%A2%98/5105%20Cookies


大概有一个初步状态的设计想法,第一维dp到第几个人,第二位dp发了多少饼干。但是人是杂乱无章的,无法进行dp。尝试将无序化为有序,看看可不可以排序。

发现越贪婪的人,我们希望他拿的饼干越多,因为少的话造成的代价大嘛,所以宁愿让贪婪度小的人去造成代价。

猜到最优方案一定是按贪婪度从大到小排序后从左到右分发饼干单调不增的。可以用微扰证明,比如假设在排序后的某个人之后的人分的饼干比这人多,发现剩下的人不会消去怨气可能会更多。对于两个人来说,通过自身贪婪度关系可以比较出这样一定是不优的。日常口胡证明毕。

所以有了顺序,$g$从大到小,dp。暴力思路是$f[i][j][k]$表示第$i$个人时发了$j$个,本人拿了$k$个的min代价。所以每次枚举$i,j,k$,再考虑和之前的大小关系,也就是枚举之前连续多少个人和他拿的饼干一样多,然后转移。

$f[i][j][k]=min\{f[i-l][j-l*k][p]+sum[i-l+1$~$i]*(i-l)\}$

然后会享受到时空双炸。然后就卡住了。。。。

lyd给的做法乍一看有点神仙。。根本想不到啊。。。但是仔细剖析一下,其本质就是对上面暴力的一种(等效)优化。优化功夫还不到家啊。。

发现原本枚举第$i$个人拿了$k$个饼干并向前枚举有多少人也拿了$k$个,这样其实是没有必要的多余计算。当第$i$个人取了$1$个饼干,向前直接枚举即可。

而假设要计算取了$k(k \geqslant 2)$个饼干的话呢,这种情况可以直接由之前推过的状态等效转移。所有人统一去掉$1$块饼干,是不是我之前推过的状态$(f[i][j-i][...])$?也就是说我之前的$j-i$块饼干分配的最优情况,再经过每人都发一块,其最优性不变,也就是$i$取了$k$个的时候的最优情况。(可以反证证明为什么之前的最优的统一加一块就是现在最优的)这等效于我暴力枚举$k$,再枚举人数。其本质是一种前缀min的不断继承

所以状态得到简化 $f[i][j]$表示第$i$个人时发了$j$个的$min$代价。然后每次每个人由选$1$个(暴力dp)和选若干个(等效转移)中取min即可。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define dbg(x) cerr<<#x<<" = "<<x<<endl
#define ddbg(x,y) cerr<<#x<<" = "<<x<<" "<<#y<<" = "<<y<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>inline char MIN(T&A,T B){return A>B?A=B,:;}
template<typename T>inline char MAX(T&A,T B){return A<B?A=B,:;}
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=+,M=+;const ll INF=1ll<<;
ll f[N][M],sum[N][N];
int ans[N],n,m,cnt;
struct thxorz{
int g,pos;
}A[N];
pii h[N][M];
inline char cmp(thxorz a,thxorz b){return a.g>b.g;} int main(){//freopen("test.in","r",stdin);//freopen("test.out","w",stdout);
read(n),read(m);
for(register int i=;i<=n;++i)read(A[i].g),A[i].pos=i;
sort(A+,A+n+,cmp);
for(register int i=;i<=n;++i)sum[][i]=sum[][i-]+A[i].g;
for(register int i=;i<=n;++i)for(register int j=;j<=i;++j)sum[j][i]=sum[][i]-sum[][j-];
for(register int i=;i<=n;++i){
for(register int j=;j<i;++j)f[i][j]=INF;f[i][i]=;
for(register int j=i+;j<=m;++j){
f[i][j]=f[i][j-i];h[i][j]=make_pair(i,j-i);
for(register int k=i-;k;--k)if(MIN(f[i][j],sum[k+][i]*k+f[k][j-(i-k)]))h[i][j]=make_pair(k,j-i+k);
}
}
printf("%lld\n",f[n][m]);int x=n;
while(x){
if(h[x][m].first==x)++cnt;
else for(register int i=h[x][m].first+;i<=x;++i)ans[A[i].pos]=cnt+;
pii tmp=h[x][m];x=tmp.first,m=tmp.second;
}
for(register int i=;i<=n;++i)printf("%d ",ans[i]);
return ;
}

CH5105 Cookies[线性DP]的更多相关文章

  1. $CH5105\ Cookies$ 线性$DP+$贪心

    CH 是很有趣的一道题 : ) Sol 第一反应就是f[i][j]表示前i个小朋友分j块饼干的最小怨气值 但是一个孩子所产生的怨气值并不固定,它与其他孩子获得饼干的情况有关 这里可以用到一个贪心,就是 ...

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

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

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

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

  4. 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门 ...

  5. 动态规划——线性dp

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

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

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

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

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

  8. nyoj44 子串和 线性DP

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

  9. 『最大M子段和 线性DP』

    最大M子段和(51nod 1052) Description N个整数组成的序列a[1],a[2],a[3],-,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M &g ...

随机推荐

  1. JS apply 、call和bind

    JS当中的call .apply.和bind 这三个方法都是js function当中自带的方法,用来改变this的指向. call()方法 语法格式: fun.call(thisArg[,arg1[ ...

  2. PJzhang:从csdn到pipal密码分析工具

    猫宁!!! 偶然看到一篇短文,是对2011年csdn泄露的约643万数据的数据汇总分析,这里做个简要总结 .   一.单一密码使用5000人以上的,14个,当时密码中有两个命名方式很有趣,dearbo ...

  3. ufile的硬盘

    参考: https://docs.ucloud.cn/compute/uhost/introduction/disk UFS: https://docs.ucloud.cn/storage_cdn/u ...

  4. [c++] 链表各类操作详解

    链表概述 链表是一种常见的重要的数据结构.它是动态地进行存储分配的一种结构.它可以根据需要开辟内存单元.链表有一个“头指针”变量,以head表示,它存放一个地址.该地址指向一个元素.链表中每一个元素称 ...

  5. 【0.3】mysql复制的日常管理维护,mysql复制常见问题处理

    [1]复制的日常管理 #复制的日常管理与维护 [1.1]show slave status\G :在从库查看从库线程状态 [1.2]flush tables with read lock;  :主从不 ...

  6. 纯前端表格控件SpreadJS V12.1 隆重登场,专注易用性,提升用户体验

    ​ 一款优秀的开发工具,在更新迭代中,除了要满足不同场景的业务需求,也需不断优化已有功能,尤其是细节方面,要能为用户带来使用体验和开发效率的提升. 作为一款备受业界专家和开发者认可的纯前端类Excel ...

  7. Oracle表概念

    对于初学者来说,对表的概念也有一定的认识.因为我们对数据库的操作,90%以上是对表的操作. 常见表的规则表(Regular table),严格意义上来说又叫 heap table(堆表),也就是我们最 ...

  8. EM 算法(一)-原理

    讲到 EM 算法就不得不提极大似然估计,我之前讲过,请参考我的博客 下面我用一张图解释极大似然估计和 EM 算法的区别 EM 算法引例1-抛3枚硬币 还是上图中抛硬币的例子,假设最后结果正面记为1,反 ...

  9. Spring的事务传播机制实例 (转)

    1,Propagation.REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中.详细解释在代码下方. 实例 员工service @Service public ...

  10. Spring 自定义注解,结合AOP,配置简单日志注解 (转)

    java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...