数形结合、单调队列+uva1451

解题思路:

     1. 这题是一道数形结合的问题, 我从《浅谈数形结合思想在信息学竞赛中的应用》学习而得算法.

     Sum[i] = a1+a2+...+ai,那么平均值ave(i,j) =(sum[j]-sum[i-1])/(j-(i-1));

     容易发现问题可以转换为:求函数function(i,Sum[i])的任意两点的最大斜率.具体可以从学习

     上面提到的《浅谈数形结合思想在信息学竞赛中的应用》讲解, 我只提出一点就是:维护下凸曲线

      例如ACM: <wbr>uva <wbr>1451 <wbr>- <wbr>Average明显斜率K(j,i)>K(k,j),K(i,k)

      无法确定更大的斜率K,j点可以说是多余的,因此要维护下凸曲线(斜率逐渐变大的曲线).

      2. 题目到这里其实可以做了,用单调队列就可以了.

   这相当于一个非递减数列,i是x坐标,sum[i]是y坐标,在这个图上找y-x>=L的斜率最大的两个点。用一个队列保存当前可能用到的起点,设i从L递增到N,我们每次在队列中加入i-L作为起点,设rear为当前队列中最后一个元素。如果发现rear和i-L这两个点的斜率小于等于rear-1和rear这两个点的斜率,也就是rear-1,rear,i-L三个点一次连成的曲线不是下凸的,就可以把rear这个点删掉了。这是因为后面再出现的点怎么都不可能和rear这个点构成最大斜率(可以自己画个图看一下)。从后删到不能删后,把i-L加到最后。不光可以从后删掉一些没用的点,还可以从前删掉一些。如果front那个点和i点的斜率小于front+1和i的斜率,那么front这个点可以删掉,因为y是单调不减的,后面再出现的点如果可能更新最大斜率的话,明显和front+1构成的斜率更优。这样又从前删掉了一些点。删完后i和当前的front就是i点作为end的最优答案。

  总之,队列中的点都是当前有可能作为起点的,是一条下凸曲线。这样就比遍历一遍省时间,省去了一些没用的点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100005;
int len,l;
char s[maxn];
int sum[maxn],q[maxn];
double k(int x,int y)
{
    return (sum[y]-sum[x])*1.0/(y-x);
}
int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%s",&len,&l,s);
        int n=strlen(s);
        sum[0]=0;
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+s[i-1]-'0';
        int front=0,rear=-1;
        double ans=0;
        int anslen=0,x=0,y=l;
        for(int i=l;i<=n;i++)
        {
            int tmp=i-l;
            while(front<rear&&k(q[rear],tmp)<=k(q[rear-1],q[rear]))rear--;
            q[++rear]=tmp;
            while(front<rear&&k(q[front],i)<=k(q[front+1],i))front++;
            double m=k(q[front],i);
            int tmplen=i-q[front]+1;
            if(m==ans&&tmplen<anslen||m>ans)
            {
                ans=m;
                anslen=tmplen;
                x=q[front],y=i;
            }
        }
        printf("%d %d\n",x+1,y);
    }
    return 0;
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值