您的位置:首页 > 其它

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

2015-02-08 11:16 579 查看
题意:

给出一个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很相似,当时第一次见到用单调栈的解法,仅仅是凭着自己的理解写的题解,现在看来写得也十分蛋疼。

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

#include <iostream>
#include <cstdio>
using namespace std;

const int maxn = 100000 + 10;
char s[maxn];
int n, L, sum[maxn], p[maxn];

int cmp(int a1, int b1, int a2, int b2)
{ return (sum[b1]-sum[a1-1]) * (b2-a2+1) - (sum[b2]-sum[a2-1]) * (b1-a1+1); }

int main()
{
//freopen("in.txt", "r", stdin);

int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &L); getchar();
for(int i = 1; i <= n; ++i) s[i] = getchar();
for(int i = 1; i <= n; ++i) sum[i] = sum[i-1] + s[i] - '0';

int i = 0, j = 0, ansL = 0, ansR = L;
for(int t = L; t <= n; ++t)
{//枚举连续子串的右端点
while(j - i > 1 && cmp(p[j-2], t-L, p[j-1], t-L) >= 0) j--;//删掉下凸点
p[j++] = t-L+1;

while(j - i > 1 && cmp(p[i], t, p[i+1], t) <= 0) i++;//找到切点使斜率最大
int c = cmp(p[i], t, ansL, ansR);
if(c > 0 || (c == 0 && t - p[i] < ansR - ansL)) { ansL = p[i]; ansR = t; }
}

printf("%d %d\n", ansL, ansR);
}

return 0;
}


代码君
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: