UVA 1451 單調隊列

【題目鏈接】
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=36267

【解題報告】
題目大意:
給你一個長度爲N的01串,求長度>=L的子串中,含有1的個數比子串長度最大的子串的左右端點。
簡單來說,就是假如我維護一個前綴和sum[i]表示前i個數中1的個數,那麼求最大的( sum[i]-sum[j] ) / (i-j)
也就是說,給定的點集中,求最大斜率。

可以分析得到:sum[i]是單調遞增的,所以如果有:

k<j<i,sum[k],sum[j],sum[i]是一個上凸的點集。
那麼j一定是不可取的起點。
因爲kj的斜率大於ji,而i後面的點p,會落在i的右上角,所以kp仍然是kp,jp,ip中最大的斜率。我們只需要保留k和i點即可

由此可知,上凸的端點可以直接被捨棄掉(注意,我們捨棄它的時候一定已經更新過它作爲終點的答案),那麼我們每加入一個端點的時候,一定是把它插入到一個下凸的點集裏。每次我們用隊列尾部的線段來更新ans值即可。
注意每次查詢的時候要有一步操作,因爲我們每次是取隊首,但是對於一個i,它和隊列某點的切線值顯然是當前最大k,但這個切點不一定是隊首,所以要將切點前的點全部出隊。如果有兩條切線重合(確實存在這樣的數據),那麼我們應當取較短的切線,所以仍然要執行出隊操作。
代碼能力比較弱,寫這道題仍然覺得繁難(wa了11次……),參考了一篇題解,這裏是採用了定義了一個line類的方式,我覺得代碼頓時清晰了很多,所以要學習這種模板。能夠最大限度的來清晰的實現自己的思路。
http://blog.csdn.net/keshuai19940722/article/details/19629545?utm_source=tuicool&utm_medium=referral
————BearChild《uva 1451 - Average(數形結合)》

【參考代碼】

#include <cstdio>
#include <cstring>
#include<iostream>
#include<deque>
#include<cmath>
using namespace std;

int N,L;
deque<int>q;
char str[100000+100];
int sum[100000+100];

double get_K(  int i, int j  )
{
      return  (sum[i]-sum[j])*1.0/(double)( i-j );
}

bool eps( double a )
{
      if(  a<-1e-6 )return true;
      else return false;
}
bool eql( double a )
{
      if( fabs(a)<1e-6 )return true;
      return false;
}

int main ()
{
    //  freopen( "uva1451.txt","r",stdin );
      int T; scanf( "%d",&T );
      while( T-- )
      {
            scanf(  "%d%d",&N,&L );
            q.clear();
            scanf( "%s", &str[1] );
            memset(  sum, 0,sizeof sum );
            for( int i=1; i<=N; i++ )
            {
                  sum[i]=sum[i-1];
                  if( str[i]=='1' ) sum[i]+=1;
            }
            double ans=-1;  //賦值爲0是不行的......
            int left=0,right=N; //這裏把right也賦值爲0,wa了一次
            for( int i=L; i<=N; i++  )
            {
                  int j=i-L;
                  while(  q.size()>1  )  // 把i-L推進去
                  {
                        int k=q[ q.size()-1 ], m=q[ q.size()-2  ];
                       // if(  get_K( j,k  ) <get_K( k,m )   )q.pop_back();
                        if(  eps( get_K(j,k)-get_K(k,m) )  )q.pop_back();
                        else break;
                  }
                  q.push_back(j);
                  while(  q.size()>1 )//找到i和隊列的切點
                  {
                        int k=q[0], m=q[1];
                      //  if(  get_K( i,k )>get_K(m,k)  )q.pop_front();
                        if(   eps(  get_K(i,k)- get_K( i,m ) )  || eql(  get_K(i,k)- get_K( i,m )  )    )q.pop_front();  //這裏,如果斜率相同應該彈出
                        else break;
                  }

                  if(  eps(ans-get_K( i, q[0] ))  ||  (eql(ans-get_K(i,q[0]))&&(i-q[0]<right-left))    )  //這裏如果不加eps判斷,那麼如果後面有相同的ans值就會更新。
                  {
                        //這裏的bug找了最久,用12 6 111000111001這組數據卡掉的,顯然如果後面有ans相同的,如果len更短那麼也應該更新
                        ans=get_K( i,q[0] );
                        left=q[0]; right=i;
                  }
            }
            printf(  "%d %d\n",left+1,right );
      }

      return 0;
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章