zoukankan      html  css  js  c++  java
  • UVa 1451

    链接:

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4197

    题意:

    给定一个长度为n的01串,选一个长度至少为L的连续子串,使得子串中数字的平均值最大。
    如果有多解,子串长度应尽量小;如果仍有多解,起点编号尽量小。序列中的字符编号为1~n,
    因此[1,n]就是完整的字符串。1≤n≤100000,1≤L≤1000。
    例如,对于如下长度为17的序列00101011011011010,如果L=7,最大平均值为6/8
    (子序列为[7,14],其长度为8);如果L=5,子序列[7,11]的平均值最大,为4/5。

    分析:

    数形结合。


    先求前缀和Si=A1+A2+…+Ai(规定S0=0),然后令点Pi=(i, Si),则子序列i~j的平均值为
    (Sj-S(i-1))/(j-i+1),也就是直线P(i-1)Pj的斜率。这样可得到主算法:从小到大枚举t,
    快速找到t'≤t-L,使得Pt'Pt斜率最大。


    对于给定的t,要找的点Pt'在Pt的左边。假设有3个候选点Pi、Pj、Pk,下标满
    足i<j<k<t,并且3个点成上凸形状(Pj为上凸点),则Pj一定不是最优点,
    换句话说,只要出现上凸的情况,上凸点一定可以忽略。

    假设已经有了一些下凸点,现在又加入了一个点,可能会使一些已有的点变为上凸点,
    这时就应当将这些上凸点删除。由于被删除的点总是原来的下凸点中最右边的若干个连续点,
    所以可以用队列来实现。

    得到下凸线之后,对于任何一个点Pt来说,最优点Pt'都在切点,
    如何求切点呢?随着t的增大,斜率也是越来越大,所以每次求出的t'只会增大,不会减小。
    因此每次增加到斜率变小时停下来即可。

    代码:

     1 #include <cstdio>
     2 
     3 const int UP = 100000 + 5;
     4 
     5 int sum[UP], que[UP];
     6 char s[UP];
     7 
     8 //L~R 的平均值为 (sum[R]-sum[L-1])/(R-L+1)
     9 inline int compare(int x1, int x2, int x3, int x4){ //比较 x1~x2 与 x3~x4 的平均值
    10     return (sum[x2]-sum[x1-1]) * (x4-x3+1) - (sum[x4]-sum[x3-1]) * (x2-x1+1);
    11 }
    12 
    13 int main(){
    14     int T;
    15     scanf("%d", &T);
    16     while(T--){
    17         int n, L;
    18         scanf("%d%d%s", &n, &L, s + 1);
    19 
    20         for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + s[i] - '0';
    21 
    22         int head = 0, tail = 0, ansL = 1, ansR = L;
    23 
    24         //que[head...tail] 为候选左端点队列
    25         for(int i = L; i <= n; i++){ //枚举右端点
    26             while(tail - head > 1 && //删除上凸点
    27                   compare(que[tail-2], i-L, que[tail-1], i-L) >= 0) tail--;
    28             que[tail++] = i - L + 1; //新的候选点
    29 
    30             while(tail - head > 1 && //更新切点
    31                   compare(que[head], i, que[head+1], i) <= 0) head++;
    32 
    33             int res = compare(que[head], i, ansL, ansR);
    34             if(res > 0 || res == 0 && ansR - ansL > i - que[head]){
    35                 ansL = que[head];  ansR = i;
    36             }
    37         }
    38         printf("%d %d
    ", ansL, ansR);
    39     }
    40     return 0;
    41 }
  • 相关阅读:
    程序员新年要实现的10个愿望
    编写超级可读代码的15个最佳实践
    LeetCode 最大子序和
    LeetCode 最大正方形
    LeetCode 买卖股票的最佳时机 II
    LeetCode 买卖股票的最佳时机
    B树和B+树
    SQL的几种连接
    LeetCode 无重复字符的最长子串
    LeetCode 翻转字符串里的单词
  • 原文地址:https://www.cnblogs.com/hkxy125/p/8126615.html
Copyright © 2011-2022 走看看