uva 1451 Average 平均值

题意: 给出一个长度为n的01序列, 要你求出一段至少长度为L的连续子序列, 该子序列的数字的平均值       最大, 多解尽量保证长度小, 在保证起点编号尽量小, 求出起点和终点编号。



/**==========================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source: UVA - 1451 Average
 *   @type: 数形结合
 *   @author: wust_ysk
 *   @blog:  http://blog.csdn.net/yskyskyer123
 *   @email: 2530094312@qq.com
 *===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn= 100000   ;
int n,L;
char s[maxn+3];//存储读取的字符串
int sum[maxn+3];//sum[x]表示[1,x]元素之和
int q[maxn+4];//队列
void pre()
{
     sum[0]=0;
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+(s[i]=='1'?1:0);
    }
}

int deltaAverage(int x1,int x2,int x3,int x4)//[x1,x2],[x3,x4]段的平均值之差,
//也可理解为线段[ (x1-1,sum[x1-1] ),(x2,sum[x2])]和线段[ (x3-1,sum[x3-1] ),(x4,sum[x4])]的斜率之差
{
    return (sum[x2]-sum[x1-1])*(x4-x3+1) -(sum[x4]-sum[x3-1])*(x2-x1+1);
}
void work()
{
    int ansL=1,ansR=n;//答案区间[ansL,ansR];
    int rear=1,front=1;
    for(int i=L;i<=n;i++)
    {
        while(front+1<rear&&deltaAverage(q[rear-2],i-L,q[rear-1],i-L)>=0 ) rear--;//易错
        //上凸点一定不会是最优点,所以啊sum[上凸点+1]不可能作为ansL,删除之。
        //因为这里[ansL,ansR]的斜率其实是ansL-1,ansR这条直线。
        //这个while循环的i-L不可以写成q[rear-1]
        //因为要删除一些不可能成为ansL的点,所以把如果i-L放入,所有不满足条件的点都删除。
        q[rear++]=i-L+1;
        while(front+1<rear&&deltaAverage(q[front],i,q[front+1],i)<=0 ) front++;
        //画图可知,切点不会往左移,切点不递减。

         int f=deltaAverage( q[front],i    ,ansL , ansR       );
        if(   f>0 || !f&& i-q[front]<ansR-ansL     )//更新答案
        {
            ansL=q[front];
            ansR=i;
        }

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

}

/*
其实枚举的方向起到了关键的作用,这是模仿代码库中的写法写的,对于每个x作为ansR,找到最优的ansL。
而不是对于每个x作为ansL,找到最优的ansR。

这个题目的障碍是时间复杂度太高,解题的关键在于:对于每个R,要深入分析[1,R-L+1]区间内点的关系。
一定是会剔除某些点的。

而且对于每个x是当作ansR还是当作ansL是很重要的。

*/
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&L);
        scanf("%s",s+1);
        pre();
        work();
    }

   return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值