HDU 3415 Max Sum of Max-K-sub-sequence

题目见此:http://acm.hdu.edu.cn/showproblem.php?pid=3415

解题思路:

  1. 这题一看就和POJ2823很相似,都是要求一段区间内的最大值,不同点有二:这里是循环的,这里是求和最大。
  2. 方程很明显:dp[i] =sum(i)-min(sum(j))其中{i-K<=j<i},要注意这里是min
  3. 观察j的范围,符合单调队列的要求,而j小于i,所以把i从小到大循环
  4. 怎么处理循环呢?处理循环比较简单的方法是扩成二倍数组,这里可以采用这个做法。
  5. 很明显求和必须要预处理,sum[i]表示到二倍扩充后的数组的i位置前的和,注意这样做的合理性在于我们只关注sum的差,即相对值,而不是绝对值
  6. 题目要求好麻烦,竟然还要输出开始和结束位置,开始各种写错啊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 }
View Code

需要注意的地方:

  1. 在处理第i个数据时,队列里必须要有i之前的所有值,所以在45行要先push进去处理第一个数据前的值
  2. 在dp模板时有一个更平常不一样的地方:17行必须是val[tail] >= _val,而不能是等于号,因为输出的start要求要输出小的
  3. 对start和end的范围处理很麻烦,注意end考虑到=N的情况,所以先-1模N后再+1
  4. 这里给一个卡了我很长时间的测试数据:

    1

    5 5

    1 1 1 0 0

  正确应为:3 1 3

每次都是一些小细节卡我很长时间,怎么办啊,总是没变法一次写对。另外跟对题目的理解不深刻有关系。愁啊……马上就考程设了,但愿出个单调队列优化的题吧,这段时间学单调队列学了好长时间

 

posted on 2013-06-02 17:24  白~  阅读(158)  评论(0)    收藏  举报

导航