版权声明: 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;
}