HDU 3415 Max Sum of Max-K-sub-sequence
题目见此:http://acm.hdu.edu.cn/showproblem.php?pid=3415
解题思路:
- 这题一看就和POJ2823很相似,都是要求一段区间内的最大值,不同点有二:这里是循环的,这里是求和最大。
- 方程很明显:dp[i] =sum(i)-min(sum(j))其中{i-K<=j<i},要注意这里是min
- 观察j的范围,符合单调队列的要求,而j小于i,所以把i从小到大循环
- 怎么处理循环呢?处理循环比较简单的方法是扩成二倍数组,这里可以采用这个做法。
- 很明显求和必须要预处理,sum[i]表示到二倍扩充后的数组的i位置前的和,注意这样做的合理性在于我们只关注sum的差,即相对值,而不是绝对值
- 题目要求好麻烦,竟然还要输出开始和结束位置,开始各种写错啊WA伤不起,不过最后终于过了
贴代码:
1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #include <cmath> 5 using namespace std; 6 7 const int MAX = 200005; 8 9 struct MQ 10 { 11 int pos[MAX], val[MAX]; 12 int head, tail, winlen; 13 void reset(const int& _winlen) 14 { head = 0, tail = -1, winlen = _winlen; } 15 void push_back(const int& index,const int& _val) 16 { 17 while(head <= tail && val[tail] >= _val) 18 tail--; 19 pos[++tail] = index; 20 val[tail] = _val; 21 while(abs(index - pos[head]) >= winlen) 22 head++; 23 } 24 int top() 25 { return val[head]; } 26 }q; 27 28 int dp[MAX], sum[MAX]; 29 int main() 30 { 31 int T, N, K, start, end; scanf("%d", &T); 32 while(T--) 33 { 34 scanf("%d %d", &N, &K); 35 sum[0] = 0; 36 int tmp; 37 for(int i=1 ; i<=N ; i++) 38 { 39 scanf("%d", &tmp); 40 sum[i] = tmp + sum[i-1]; 41 } 42 for(int i=1 ; i<=K ; i++) 43 sum[i+N] = sum[N] + sum[i]; 44 q.reset(K); 45 for(int i=1 ; i<=K ; i++) 46 q.push_back(i, sum[i]); 47 int _max = -0xfffffff; 48 start = end = -1; 49 for(int i=1 ; i<=N ; i++) 50 { 51 dp[i] = sum[i+K] - q.top(); 52 int ns = q.pos[q.head] % N + 1, ne = (i + K + N - 1) % N + 1; 53 if(dp[i] == _max) 54 { 55 if(ns < start) 56 { 57 start = ns; 58 end = ne; 59 } 60 else if(ns == start && abs(ne - ns) < abs(end - start)) 61 { 62 start = ns; 63 end = ne; 64 } 65 } 66 else if(dp[i] > _max) 67 { 68 _max = dp[i]; 69 start = ns; 70 end = ne; 71 } 72 q.push_back(i+K, sum[i+K]); 73 } 74 printf("%d %d %d\n", _max, start, end); 75 } 76 }
需要注意的地方:
- 在处理第i个数据时,队列里必须要有i之前的所有值,所以在45行要先push进去处理第一个数据前的值
- 在dp模板时有一个更平常不一样的地方:17行必须是val[tail] >= _val,而不能是等于号,因为输出的start要求要输出小的
- 对start和end的范围处理很麻烦,注意end考虑到=N的情况,所以先-1模N后再+1
- 这里给一个卡了我很长时间的测试数据:
1
5 5
1 1 1 0 0
正确应为:3 1 3
每次都是一些小细节卡我很长时间,怎么办啊,总是没变法一次写对。另外跟对题目的理解不深刻有关系。愁啊……马上就考程设了,但愿出个单调队列优化的题吧,这段时间学单调队列学了好长时间
浙公网安备 33010602011771号