欢迎使用CSDN-markdown编辑器

UVA1451

原文链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4197
题意:给定一个长度为n的01串,计算出长度不小于L的连续子串的最大平均值。
要求:如果多解,长度尽量小,如果长度相同,起点编号应尽量小。
(1 <= n <= 100000, 1 <= L <= 1000)

例如:当n = 17, L = 7时,对于00101011011011010而言,最大平均值为6/8(子序列为[7, 14],长度为8)。

分析:
先求的前缀和Si = A1+A2+…+Ai, (S0 = 0),然后令点Pi = (i, Si), 则子序列i~j的值为(Sj - Si-1)/(j-(i-1)), 也就是直线Pi-1Pj的斜率K;

接下来只需判断点的凹凸情况即可。
首先,去掉所有上凸点,然后再判断剩下的所有点中切线斜率最大的点,即为所求。

// ConsoleApplication22.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"//VS2015编译时需加上预编译头文件,其他编译器忽略
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 5;

int n, L;
char s[maxn];
int sum[maxn], p[maxn];//sum[maxn]用来存放前缀和,
                       //p[maxn]用来存放暂时可行的下凸点

int Cmp_Ave(int x1, int x2, int x3, int x4)
{
    return (sum[x2] - sum[x1 - 1])*(x4 - x3 + 1) - (sum[x4] - sum[x3 - 1])*(x2 - x1 + 1);
}
//该表达式通俗一点,可以表示为:(y2-y1)*(x4-x3)-(y4-y3)*(x2-x1);
//也就是(y2-y1)/(x2-x1)-(y4-y3)/(x4-x3),表示的是两条直线之间斜率的大小关系
//等于0,表示斜率相等
//大于0,表示x1x2的斜率大于x3x4
//小于0,表示x1x2的斜率小于x3x4

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cin >> n >> L >> s + 1;
        sum[0] = 0;//初始化为0
        //计算前缀和
        for (int i = 1; i <= n; i++)sum[i] = sum[i - 1] + s[i] - '0';

        int ansL = 1, ansR = L;
        int i = 0, j = 0;
        for (int t = L; t <= n; t++)
        {
            while (j - i > 1 && Cmp_Ave(p[j - 2], t - L, p[j - 1], t - L) >= 0)j--;//去除上凸点
            p[j++] = t - L + 1;//加入新的候选点
            //比较候选点与t点,更新斜率更大的点
            while (j - i > 1 && Cmp_Ave(p[i], t, p[i + 1], t) <= 0)i++;
            int c = Cmp_Ave(p[i], t, ansL, ansR);
            //如果c大于0,表示找到更优答案,更新
            //如果c等于0,则要判断序列是否更优
            if (c > 0 || c == 0 && t - p[i] < ansR - ansL)
            {
                ansR = t;
                ansL = p[i];
            }
        }
        cout << ansL << " " << ansR << endl;
    }
    return 0;
}

这道题运用了数学中关于斜率的知识,最好需要结合图形,推敲一遍。
上凸下凸

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值