1200

单调队列优化DP


新司机上路,请多指教
对于一些复杂度高的DP,形如 d p [ i ] [ j ] = m a x { s u m [ i ] + f ( k ) } dp[i][j]=max\{sum[i]+f(k)\} dp[i][j]=max{sum[i]+f(k)}(sum[i]是预处理量, f ( k ) f(k) f(k)是关于k的已知函数),可以用单调队列处理出 f ( k ) f(k) f(k)的最值,直接进行调用。

空说很难说清,看例题poj1821,题目如下:
Fence
有n个连续的篱笆,m个工人,每个工人i有一个位置Si,粉刷每个篱笆的工钱Pi,可粉刷的最大篱笆数Li(必须包括自身位置的篱笆,且粉刷的篱笆连续),假如你是工人头头,请合理分配每个工人的任务,使总工钱最大。(可以原题K这里为m)
样例
Input
n m
L1 P1 S1
L2 P2 S2

Lm Pm Sm

这个问题可以用递推。
为了方便根据位置排序,我们可以设一个结构体 p [ i ] p[i] p[i]存三个值, d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个人粉刷 j j j个篱笆(可以有的不粉刷)后的总工钱,发现它的递推式可以写为 d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ k ] + p [ i ] . p × ( j − k ) } dp[i][j]=max\{dp[i-1][j],dp[i-1][k]+p[i].p\times(j-k)\} dp[i][j]=max{dp[i1][j],dp[i1][k]+p[i].p×(jk)},而且k的范围为 m a x { 0 , p [ i ] . s − p [ i ] . l + 1 , p [ i − 1 ] . s } < k < m i n { p [ i ] . s + p [ i ] . l , j } max\{0,p[i].s-p[i].l+1,p[i-1].s\}<k<min\{p[i].s+p[i].l,j\} max{0p[i].sp[i].l+1p[i1].s}<k<min{p[i].s+p[i].lj}
可以用三重循环枚举i,j,k。

改变一下递推式,可以变为 d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] , p [ i ] . p × j + ( d p [ i − 1 ] [ k ] − p [ i ] . p × k ) } dp[i][j]=max\{dp[i-1][j],p[i].p\times j+(dp[i-1][k]-p[i].p\times k)\} dp[i][j]=max{dp[i1][j],p[i].p×j+(dp[i1][k]p[i].p×k)}
对于max中逗号后的内容, p [ i ] . p × j p[i].p\times j p[i].p×j可直接求出,后面的 d p [ i − 1 ] [ k ] − p [ i ] . p × k dp[i-1][k]-p[i].p\times k dp[i1][k]p[i].p×k则相当于 f ( k ) f(k) f(k)。因为随着 j j j的变大, k k k的范围也随之改变,那么我们只需按 f ( k ) f(k) f(k)设置 k k k的单调递减队列,每次调用前把不符合的 k k k值去除,符合的k加入,选出队列中符合的第一个 k k k。(每次 i i i改变时记得清空队列)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值