Average UVA - 1451

题目链接:https://cn.vjudge.net/problem/UVA-1451

题目大意:
给出一段序列,找出长度大于L,同时一的个数除以长度最大的起点和终点,比值相同时,长度尽量短,两者相同时,起点尽量小。

思路:

题目要求的比值就是(sum[j]-sum[i-1])/(j-i+1),看到这个是不是很熟悉,没错这个就是大家很了解的斜率公式,那么题目就变成了,求斜率最大的两点,其中两点距离大于等于L

其中横坐标就代表序列起点和终点,纵坐标就是前缀和sum[j]和sum[i],接下来的问题就是怎么去求斜率最大的两个点了,其实这道题别人说时斜率优化,但是我并不知道斜率优化

的通解(也不知道有没有),这道题因为x,和y时具有非严格单调性的,所以比较特殊点,可以用单调队列维护的(我这里用数组模拟),对于当前已经在队列中的点,对于将要新添加的点,如果他的添加如果会出现上凸点,那么就将上凸点删掉因为其他的点一定比上凸点优(别忘了,点具有单调性),然后对于队列中的点我们怎么去找到最优的点呢,

此时队列中的点只有两种情况,一种是存在下凸点,一种是与终点斜率相同,这两种情侣都是要删除队首的点直到找到下凸点,或者只剩下一个点,这时队首的点就是此时的最优解,那么为什么可以删去下凸点之前的点,因为对于终点与队列的两个点组成的三个点,下凸点一定是与终点的斜率大的,那么对于之后的终点是否也满足呢,虽然不是满足的,

但是此时最大斜率明显没有之前的大,所以要删除下凸点之前的点,(注意这里的下凸点的寻找是与终点比较寻找的),一句话总结来说就是对于队首的点的删除是因为,终点的改变会影响斜率的最大值,也就是说与折线的切点会改变,对于队尾的删除是因为,新加入的点可能会改变下凸性

ac代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<sstream>
using namespace std;
const int maxn = 1e5 + 10;
char s[maxn];
int sum[maxn];
int T,n;
int que[maxn];
int cmp(int l1,int r1,int l2,int r2)
{
    if(((sum[r1]-sum[l1-1])*(r2-l2+1))>((sum[r2]-sum[l2-1])*(r1-l1+1)))
        return 1;
    else if(((sum[r1]-sum[l1-1])*(r2-l2+1))==((sum[r2]-sum[l2-1])*(r1-l1+1)))
        return 0;
    else
        return -1;
}
int main()
{
    int L;
    scanf("%d",&T);
    while(T--)
    {
        memset(sum,0,sizeof(sum));
        scanf("%d%d",&n,&L);
        scanf("%s",s+1);
       // cout<<s+1<<endl;
        for(int i = 1 ;i <=n ;i++)
        {
            sum[i] = sum[i-1]+s[i]-'0';
        }
        int ansL = 1;
        int ansR = L;
        int l = 0;
        int r = 0;
        for(int i = L;i<=n;i++)
        {
           // cout<<i-L+1<<' '<<sum[i-L+1]<<endl;
            while(r-l>1&&cmp(que[r-2],i-L,que[r-1],i-L)>=0)
            {
                r--;
            }
            que[r++] = i-L+1;
            while(r-l>1&&cmp(que[l],i,que[l+1],i)<=0)
                l++;
            if(cmp(que[l],i,ansL,ansR)>0||(cmp(que[l],i,ansL,ansR)==0&&(i-que[l]<ansR-ansL)))
            {
                //cout<<que[l]<<' '<<i<<' '<<ansL<<' '<<ansR<<endl;
                ansL = que[l];
                ansR = i;
            }
        }
        cout<<ansL<<' '<<ansR<<endl;
    }
    return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值