算法竞赛进阶指南第五章 线性DP——Cookies

在这里插入图片描述
这是贪心和动态相结合的一道题。
要找到一种好的分配方式,使得怨气值之和最小,**贪婪度大的孩子如果能够给他尽可能多的饼干,可以尽量使得最后怨气值之和最小。**即按怨气值从大到小排序。
这样的贪心是否正确呢?——可以用邻项交换法来证明一下


设先有一种分配饼干的方案是,设g[i]<g[i+1]
1 2 3 …… i i+1 …… n (1)
1 2 3 …… i+1 i …… n (2)
交换i 和 i+1位置的孩子,发现对于前1 ~ i个 和 从i+2 ~ n个,这样的交换对于这些孩子的怨气值没有影响
有影响的就是:
(1)中去除相同的部分,不同的是:g[i+1]*1
(2) 中去除相同的部分,不同的是:g[i]*1
又因为 g[i]<g[i+1] 所以(2)中的最终怨气值会更小

不断的交换,最后会使得怨气值按从大到小的顺序排列更优

————————————————————————————

大体方案有了,具体每个孩子分多少块呢?可以借助dp来处理

定义f(i,j)表示前i个孩子分j块时,这i个孩子的怨气值总值最小。
在考虑dp的转移时,我们考虑是由那种状态能够达到f(i,j)的状态(怎么来的),也可以考虑下一个状态可以达到哪些情况(往哪去),这里我们考虑下一个状态有哪些,即第i+1个小朋友该如何分配饼干数。
→ 情况1:第i+1个小朋友分的比第i个小,则a[i+1]=i,即前面由i个小朋友拿到的饼干比他多(原因每一个小朋友饼干数的分配数量呈非严格单调下降,第i个小朋友比第i+1个多,则前面的不会小于第i个小朋友的饼干数) 如图。
在这里插入图片描述

→ 情况2:第i+1个小朋友拿到的饼干数与第i个小朋友相同,此时还需要算出前面有多少个小朋友拿到的饼干数与第i个相同。
…………………………………………………………………………
不好转移,做一个等价处理:
如果第i个拿到饼干数 > 1,处理方式为:令她及前面的孩子的饼干数均减少1个,即将j-i个饼干分给前i个孩子,因为这样处理前后的大小关系不会发现变化,从而使得怨气值之和也不会发生变化。
如果第i个拿到的饼干数=1,处理方式为:枚举她前面有多少个孩子拿的都是1个饼干,设分界点为k,即1 ~ k 个孩子拿到饼干数>1,编号k+1 ~ i的孩子饼干数=1
此情况下的状态转移方程为:
在这里插入图片描述

综上,
在这里插入图片描述

这道题启发我们,有时可以通过额外的算法确定DP状态的计算顺序,有时可以在状态空间中运用等效手法对状态进行缩放。在本题中,我们就利用贪心策略,在DP前对Ⅳ个孩子执行排序,使他们获得的饼干数单调递减。我们还利用相对大小的不变性,把第 i+1个孩子获得的饼干数先缩放到1,再考虑i前面有几个孩子获得的饼干数量相等,使需要计算的问题得到了极大的简化,容易进行维护、转移。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 35, M = 5010;

int n, m;
//g[i] 表示第 i 个小孩的贪婪度
//c[i] 表示贪婪度第 i 大的小孩在原序列中排第 c[i] 个
//s[i] 表示 c[i] 的前缀和
int g[N], c[N]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值