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. RDD实例

    实例一: teacher.log http://bigdata.baidu.cn/zhangsan http://bigdata.baidu.cn/zhangsan http://bigdata.ba ...

  2. 《Java开发手册》

    一. 编程规约 (一) 命名风格 [强制]抽象类命名使用Abstract或Base开头:异常类命名使用Exception 结尾:测试类 命名以它要测试的类的名称开始,以Test结尾. [强制]POJO ...

  3. Unity打包出来的App 跳转其他 App

    void Start()//在android测试成功 { bool fail = false; string bundleId = "跳转的包名";//包名称 AndroidJav ...

  4. [转帖]$PWD 和 $(pwd)

    $PWD 和 $(pwd) https://blog.csdn.net/shaojwa/article/details/51894980 细节决定成败.   注意两个效果一样,但是注意大小写,PWD是 ...

  5. [转帖]Linux /tmp目录下执行脚本失败提示Permission denied

    Linux /tmp目录下执行脚本失败提示Permission denied https://www.cnblogs.com/linyfeng/p/11087655.html 国产化的环境上 就有一个 ...

  6. [转帖] Linux下面计算文件数量的方法

    Linux命令-查看目录下文件个数 2018年07月04日 10:37:07 sand_clock 阅读数 2002    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blo ...

  7. robot-framework 利用evaluate关键字生成随机数

    robot-framework 利用evaluate关键字生成随机数 最近用RF(robot-framework简称)操作MangoDB,需要直接将数据写到数据库里,又不想每次写的数据完全相同,就想到 ...

  8. C++练习 | 最长公共字符串(DP)

    HDU 1159.Common Subsequence #include<iostream> #include<stdio.h> #include<string> ...

  9. java native本地方法详解(转)

    文章链接出处: 详解native方法的使用 自己实现一个Native方法的调用 JNI 开始本篇的内容之前,首先要讲一下JNI.Java很好,使用的人很多.应用极 广,但是Java不是完美的.Java ...

  10. Eclipce远程调试

    1.注意: root权限启动的进程不支持远程调试,也有很多隐患,支持最高权限,应用普通用户启动,又原先安装Tomcat是使用的root权限,普通用户对root安装的软件没有执行权限,可以修改相应权限, ...