HDU - 6444 -- Neko's loop (单调队列优化dp)

版权声明: https://blog.csdn.net/moon_sky1999/article/details/82288046

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=6444

先来几组对拍数据:

Input:

20
10 38 24 7
-2 6 -9 8 5 2 -4 0 -1 -2
10 66 7 9
-5 6 7 -2 -1 -7 -2 -10 9 0
10 78 19 9
1 6 -5 0 5 6 2 1 -2 5
10 17 0 3
-1 -3 -7 -4 -9 -4 -1 -3 6 6
10 0 18 3
6 5 1 7 -7 4 -6 -8 1 -3
10 1 12 3
-6 4 6 -3 8 8 -4 8 -9 1
10 6 22 8
5 0 4 3 -6 -6 -10 -9 -2 3
10 42 25 1
-8 -4 6 7 9 -1 5 4 -5 -3
10 99 10 5
3 -10 -6 6 0 0 -5 9 4 5
10 41 28 6
-3 6 -7 -4 -5 -5 7 -8 1 -7
10 5 7 4
-1 -7 5 -4 -5 -3 9 4 4 7
10 96 10 9
6 6 -2 -6 -1 8 5 6 -4 -8
10 32 2 7
-3 -4 0 8 -3 -8 -7 3 3 3
10 46 6 7
2 -2 -1 4 -4 -1 -6 2 -8 -8
10 34 17 7
1 -4 8 1 -7 0 4 4 4 1
10 26 16 6
5 -7 9 -4 -8 -9 -8 1 0 -8
10 38 25 7
-8 8 -4 -6 9 -6 0 -2 -4 0
10 80 11 6
-7 6 5 8 2 8 -8 -6 1 0
10 69 6 6
5 0 1 8 1 9 4 6 7 -4
10 38 28 6
-4 0 -6 3 -6 7 -6 -10 9 -10

Output:

Case #1: 17
Case #2: 49
Case #3: 35
Case #4: 17
Case #5: 0
Case #6: 0
Case #7: 0
Case #8: 0
Case #9: 49
Case #10: 34
Case #11: 0
Case #12: 74
Case #13: 24
Case #14: 38
Case #15: 3
Case #16: 17
Case #17: 21
Case #18: 40
Case #19: 41
Case #20: 29

首先,0~n的所有划分一定是两两不相交的。因而只可能选择某个划分的全部或部分元素。

对于每个划分,由于是一个环,可以先将其复制一份,使其长度变为len*2。

因而可以转化为在len*2的序列中选一个长度<=len的区段,使区段和最大,此过程可以用dp+单调队列来处理。

代码(单调队列用STL的list实现):

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <list>

#define ll long long
#define ull unsigned long long
#define BUG printf("************\n")
using namespace std;

const int maxn = 1e6 + 10;

ll n, s, k, m, sum[maxn], a[maxn], b[maxn];
list<int> q;
bool vis[maxn];

ll dp(int len, int cnt) {
    q.clear();
    ll ans = 0;
    for (int i = 1; i <= cnt; ++i) {
        sum[i] = sum[i - 1] + b[i];
    }
    for (int i = 1; i <= cnt; ++i) {
        while (!q.empty() && sum[q.front()] > sum[i])
            q.pop_front();
        q.push_front(i);
        while (!q.empty() && i - len > q.back())
            q.pop_back();
        if (i > 1)ans = max(ans, sum[i] - sum[q.back()]);
        else ans = max(ans, sum[i]);
    }
    return ans;
}

int main() {
    int _;
    scanf("%d", &_);
    for (int sce = 1; sce <= _; ++sce) {
        scanf("%lld%lld%lld%lld", &n, &s, &m, &k);
        for (int i = 0; i < n; ++i)
            scanf("%lld", &a[i]);
        ll tot = 0;
        ll ans = 0;
        memset(vis, 0, sizeof(vis));
        for (int i = 0; i < n; ++i) {
            tot = 0;
            if (vis[i] == 0) {
                int cnt = 0;
                ll ss = 0;
                for (int j = i; vis[j] == 0; j = (j + k) % n) {
                    vis[j] = 1;
                    b[++cnt] = a[j];
                    ss += b[cnt];
                }
                for (int j = 1; j <= cnt; ++j)b[cnt + j] = b[j];
                tot = max(tot, dp(cnt, cnt * 2));
                if (ss > 0) {
                    tot = max(tot + ss * (m / cnt - 1), dp(m % cnt, cnt * 2) + (m / cnt) * ss);
                }
            }
            ans = max(ans, tot);
        }
        printf("Case #%d: %lld\n", sce, max(0ll, s - ans));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/moon_sky1999/article/details/82288046
今日推荐