单调队列优化动态规划

Posted oierprime

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单调队列优化动态规划相关的知识,希望对你有一定的参考价值。

先来看这道题:

USACO 2011 Open Gold Mowing the Lawn 修剪草坪

After winning the annual town competition for best lawn a year ago,
Farmer John has grown lazy; he has not mowed the lawn since then
and thus his lawn has become unruly. However, the competition is
once again coming soon, and FJ would like to get his lawn into
tiptop shape so that he can claim the title.

Unfortunately, FJ has realized that his lawn is so unkempt that he
will need to get some of his N (1 <= N <= 100,000) cows, who are
lined up in a row and conveniently numbered 1..N, to help him. Some
cows are more efficient than others at mowing the lawn; cow i has
efficiency E_i (0 <= E_i <= 1,000,000,000).

FJ has noticed that cows near each other in line often know each
other well; he has also discovered that if he chooses more than K
(1 <= K <= N) consecutive (adjacent) cows to help him, they will
ignore the lawn and start a party instead. Thus, FJ needs you to
assist him: determine the largest total cow efficiency FJ can obtain
without choosing more than K consecutive cows.

显然这是一道动态规划题目,我们先简化题目。题意是这样的:

FJ 有 N(1≤N≤100000) 只排成一排的奶牛,编号为 1...N。每只奶牛的效率是不同的, 奶牛 i 的效率为 Ei(0≤Ei≤1000000000)。靠近的奶牛们很熟悉,因此,如果 FJ 安排超过 K(1≤K≤N) 只连续的奶牛,那么,这些奶牛就会罢工去开派对。现在 FJ 需要你帮助计算可以得到的最大效率。

如果我们用 dp[i] 来表示前i头奶牛的最大效率,那么我们可以写出这样的转移方程:

dp[i]=max?{dp[j?1]+sum[i]?sum[j]} (i?K≤j≤i)

这样的转移方程的时间复杂度是O(NK),观察这道题的数据量,相当于O(N^2),显然是不行的。

仔细观察,实际上对于 dp[i] 来说,我们需要找到一个决策 j(i?K≤j≤i) 使得 dp[j?1]?sum[j] 最大化。再看,不难发现,i和j都是单调递增的,这让我们联想到以前做过的Sliding Windows,窗户的两边也是单调的对吧。所以这道题我们其实也可以用单调队列来优化。我们用这个单调队列来维护这个下标j的位置。

 1 #include <stdio.h>
 2 
 3 typedef long long LL;
 4 const int maxn = 100010;
 5 LL dp[maxn], sum[maxn];
 6 int que[maxn], E[maxn];
 7 int head, tail;
 8 LL max(LL a, LL b) {
 9     return a > b? a : b;
10 }
11 void add(int j) {
12     while (head < tail && dp[j - 1] - sum[j] >= (que[tail - 1] > 0 ? dp[que[tail - 1] - 1] : 0) - sum[que[tail - 1]]) {
13         --tail;
14     }
15     que[tail++] = j;
16 }
17 void del(int j) {
18     if (head < tail && que[head] == j) {
19         ++head;
20     }
21 }
22 int main() {
23     int n, k;
24     scanf("%d %d", &n, &k);
25     sum[0] = 0;
26     for (int i = 1; i <= n; ++i) {
27         scanf("%d", &E[i]);
28         sum[i] = sum[i - 1] + E[i];
29     }
30     dp[0] = 0;
31     que[tail++] = 0;
32     for (int i = 1; i <= n; ++i) {
33         add(i);
34         del(i - k - 1);
35         int j = que[head];
36         dp[i] = (j > 0 ? dp[j - 1] : 0) + sum[i] - sum[j];
37     }
38     LL ans = max(dp[n], dp[n - 1]);
39     printf("%lld
", ans);
40     return 0;
41 }

 














以上是关于单调队列优化动态规划的主要内容,如果未能解决你的问题,请参考以下文章

Vijos1834 NOI2005 瑰丽华尔兹 动态规划 单调双端队列优化

BZOJ_1010_[HNOI2008]_玩具装箱toy_(斜率优化动态规划+单调队列)

BZOJ_1096_[ZJOI2007]_仓库建设_(斜率优化动态规划+单调队列+特殊的前缀和技巧)

$Dynamic Planning Optimization$ 关于动态规划的优化方案(%$color{red}{rqy}$)

动态规划入门——多重背包与单调优化,从此登堂入室

动态规划