数形结合、单调队列+uva1451

来源:互联网 发布:cpa题库软件 编辑:程序博客网 时间:2024/05/12 20:15

解题思路:

     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;}



0 0