UVa 1451 (数形结合 单调栈) Average

题意:

给出一个01串,选一个长度至少为L的连续子串,使得串中数字的平均值最大。

分析:

能把这道题想到用数形结合,用斜率表示平均值,我觉得这个想法太“天马行空”了

首先预处理子串的前缀和sum,如果在坐标系中描出(i, sum[i])这些点的话。

所求的平均值就是两点间的斜率了,具体来说,在连续子串[a, b]中,有sum[b]-sum[a-1]个1,长度为b-a+1,所以平均值为(sum[b]-sum[a-1])/(b-a+1)

所以就把问题转化为:求两点横坐标之差至少为L-1,能得到的最大斜率。

这道题和HDU 5033很相似,当时第一次见到用单调栈的解法,仅仅是凭着自己的理解写的题解,现在看来写得也十分蛋疼。

还是紫书上讲得清楚透彻。

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 const int maxn = 100000 + 10;
 6 char s[maxn];
 7 int n, L, sum[maxn], p[maxn];
 8 
 9 int cmp(int a1, int b1, int a2, int b2)
10 { return (sum[b1]-sum[a1-1]) * (b2-a2+1) - (sum[b2]-sum[a2-1]) * (b1-a1+1); }
11 
12 int main()
13 {
14     int T;
15     scanf("%d", &T);
16     while(T--)
17     {
18         scanf("%d%d", &n, &L); getchar();
19         for(int i = 1; i <= n; ++i) s[i] = getchar();
20         for(int i = 1; i <= n; ++i) sum[i] = sum[i-1] + s[i] - '0';
21 
22         int i = 0, j = 0, ansL = 0, ansR = L;
23         for(int t = L; t <= n; ++t)
24         {//枚举连续子串的右端点
25             while(j - i > 1 && cmp(p[j-2], t-L, p[j-1], t-L) >= 0) j--;//删掉上凸点
26             p[j++] = t-L+1;
27 
28             while(j - i > 1 && cmp(p[i], t, p[i+1], t) <= 0) i++;//找到切点使斜率最大
29             int c = cmp(p[i], t, ansL, ansR);
30             if(c > 0 || (c == 0 && t - p[i] < ansR - ansL)) { ansL = p[i]; ansR = t; }
31         }
32 
33         printf("%d %d
", ansL, ansR);
34     }
35 
36     return 0;
37 }
代码君
原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4279797.html