1. 程式人生 > >HDU 3415 Max Sum of Max-K-sub-sequence(長度不超過k的最大連續子序列和,單調佇列)

HDU 3415 Max Sum of Max-K-sub-sequence(長度不超過k的最大連續子序列和,單調佇列)

題目連結:
HDU 3415 Max Sum of Max-K-sub-sequence
題意:
n個數,首尾相連,求長度不超過k的最大連續子序列和。
資料範圍:1n100000,1kn
分析:
因為考慮首尾相連,所以我們把n個數看成2n個數,其中data[n+]=data[i]。求個字首和sum[]。我們考慮以i為結尾的長度不超過k的最大連續子序列和。顯然是sum[i]min(sum[j]),j[ik,i1].那麼問題就轉化為求區間最小值了,所以我們維護一個單調非遞增佇列即可。注意下邊界。

#include <stdio.h>
#include <string.h>
#include <algorithm> #include <math.h> #include <climits> using namespace std; typedef long long ll; const int MAX_N = 100010 * 2; int T, n, k, head, tail, st, ed; ll data[MAX_N], sum[MAX_N]; int que[MAX_N]; int main() { scanf("%d", &T); while (T--) { scanf("%d%d"
, &n, &k); sum[0] = 0; for (int i = 1; i <= n; ++i) { scanf("%lld", &data[i]); data[i + n] = data[i]; } ll ans = data[1], tmp; st = 1, ed = 1; for (int i = 1; i <= 2 * n; ++i) { sum[i] = sum[i - 1] + data[i]; if
(i <= k) { if (sum[i] > ans) { ans = sum[i], ed = i; } } } // 維護單調遞增佇列,存字首和 head = tail = 0; que[tail++] = 1; for (int i = 2; i <= n + k; ++i) { // 剔除在i之前距離超過k的字首和 while (head != tail && i - que[head] > k) { ++head; } // 剔除在i之前字首和大的字首和 while (head != tail && sum[i] <= sum[que[tail - 1]]) { --tail; } int flag = -1; if (i <= k && sum[que[head]] >= 0) { tmp = sum[i], flag = 1; } else if (head == tail) { tmp = data[i], flag = 2; } else { tmp = sum[i] - sum[que[head]]; flag = 3; } if (tmp > ans) { ans = tmp; if (flag == 1) st = 1; else if (flag == 2) st = i; else st = que[head] + 1; ed = i; } que[tail++] = i; //printf("i = %d ans = %d:\n", i, ans); /* for (int j = head; j < tail; ++j) { printf("%d ", sum[que[j]]); } printf("\n"); */ } if (st > n) st -= n; if (ed > n) ed -= n; printf("%lld %d %d\n", ans, st, ed); } return 0; }