1. 程式人生 > >單調隊列優化動態規劃

單調隊列優化動態規劃

style 單調隊列 best then ans farm void urn open

先來看這道題:

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\n", ans);
40     return 0;
41 }

單調隊列優化動態規劃