CH5105 Cookies[线性DP]
大概有一个初步状态的设计想法,第一维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]的更多相关文章
- $CH5105\ Cookies$ 线性$DP+$贪心
CH 是很有趣的一道题 : ) Sol 第一反应就是f[i][j]表示前i个小朋友分j块饼干的最小怨气值 但是一个孩子所产生的怨气值并不固定,它与其他孩子获得饼干的情况有关 这里可以用到一个贪心,就是 ...
- LightOJ1044 Palindrome Partitioning(区间DP+线性DP)
问题问的是最少可以把一个字符串分成几段,使每段都是回文串. 一开始想直接区间DP,dp[i][j]表示子串[i,j]的答案,不过字符串长度1000,100W个状态,一个状态从多个状态转移来的,转移的时 ...
- Codeforces 176B (线性DP+字符串)
题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28214 题目大意:源串有如下变形:每次将串切为两半,位置颠倒形成 ...
- 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门 ...
- 动态规划——线性dp
我们在解决一些线性区间上的最优化问题的时候,往往也能够利用到动态规划的思想,这种问题可以叫做线性dp.在这篇文章中,我们将讨论有关线性dp的一些问题. 在有关线性dp问题中,有着几个比较经典而基础的模 ...
- POJ 2479-Maximum sum(线性dp)
Maximum sum Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 33918 Accepted: 10504 Des ...
- poj 1050 To the Max(线性dp)
题目链接:http://poj.org/problem?id=1050 思路分析: 该题目为经典的最大子矩阵和问题,属于线性dp问题:最大子矩阵为最大连续子段和的推广情况,最大连续子段和为一维问题,而 ...
- nyoj44 子串和 线性DP
线性DP经典题. dp[i]表示以i为结尾最大连续和,状态转移方程dp[i] = max (a[i] , dp[i - 1] + a[i]) AC代码: #include<cstdio> ...
- 『最大M子段和 线性DP』
最大M子段和(51nod 1052) Description N个整数组成的序列a[1],a[2],a[3],-,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的.如果M &g ...
随机推荐
- Java Applet基础——输出HelloWorld
前言: 我自己不专用applet,仅仅是了解一下,如果有不对的地方,还望大家多多帮助~谢谢! 一. 准备环境 1. 安装Java的编译环境(另行百度哦~) 2. 下载 applet.jar ,放在项目 ...
- ServletContainerInitializer
在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能. 每个框 ...
- python基础之元祖tuple
元祖是只读列表,不可哈希,可循环查询,可切片*儿子不能改,孙子可更改--元祖里面单个元素不能更改---元祖内列表可更改增:tu1+tu2查:tu1[index] tu1[start_index:end ...
- 【Python开发】Python中的class继承
继承是面向对象的重要特征之一,继承是两个类或者多个类之间的父子关系,子进程继承了父进程的所有公有实例变量和方法.继承实现了代码的重用.重用已经存在的数据和行为,减少代码的重新编写,python在类名后 ...
- [转帖]忘记了MariaDB root密码的解决办法
忘记了MariaDB root密码的解决办法 https://www.cnblogs.com/liaojie970/p/6126322.html 貌似跟 mysql 是一样的. 1.停掉mariaDB ...
- round函数——银行家舍入算法
在处理四舍五入时,相信大部分人会使用math.round函数(不同的语言应该都有).有没有考虑过,这个函数是不是自己所需要的? po主碰到的问题是用来计算平均分.有个顶真的学生反映,明明是86.5,怎 ...
- Oacle常用语句
1.建表语句 ) NOT NULL, region_id ) NOT NULL, salesperson_id ) NOT NULL, ) NOT NULL, ) NOT NULL, tot_orde ...
- 小记---------spring框架之IOC理解
Spring是一个开源框架,是一个轻量级的Java开发框架. Spring的核心是控制发转(IOC)和面向切面(AOP) 控制发转(IOC):指的是 对象的创建权反转(交给)给 Spring. 作 ...
- 2019中山纪念中学夏令营-Day20[JZOJ] T1旅游详解
2019中山纪念中学夏令营-Day20[JZOJ] 提高组B组 Team_B组 T1 旅游 Time Limits: 2000 ms Memory Limits: 262144 KB Descrip ...
- nginx配置一般优化参数
#user nobody; worker_processes 2; # CPU亲和力,worker_processes最多开启8个,注意写法 worker_cpu_affinity 01 10; wo ...