2012年8月2日 星期四

斜率優化

今天多虧了J睿的福,我很清楚的了解了斜率優化的基礎,

並自己透過概念,想出一套錯誤的演算法,再經J睿的修正,得出這個算法的正確形象

感謝最愛J!NN的J1SS睿~~~>//////<

他並不是很難,看到下面這個基本的斜率優化code就知道了,精神不過五行。

//By momo
#include<cstdio>
int s[100010],dq[100010],f,e,r,L,R,x,y,t,n,k,i;
bool crs( int a,int b,int c ){
    return 1ll*(s[b]-s[a])*(c-b)>1ll*(s[c]-s[b])*(b-a);
}
int main(){
    scanf("%d",&t);
    while( t-- ){
        scanf("%d%d\n",&n,&k);
        for( s[0]=f=e=y=0,i=x=1; i<=n;i++) r=getchar(),s[i]=s[i-1]+r-'0';
        for( i=k; i<=n; i++ ){
            while( f<e-1 && crs(dq[e-2],dq[e-1],i-k) ) e--; dq[e++]=i-k;
            while( f<e-1 && !crs(dq[f],dq[f+1],i) ) f++;
            if( 1ll*y*(i-dq[f])<1ll*x*(s[i]-s[dq[f]]) 
             || 1ll*y*(i-dq[f])==1ll*x*(s[i]-s[dq[f]]) && i-dq[f]<R-L )
                x=i-dq[f], y=s[i]-s[dq[f]], L=dq[f], R=i;
        }
        printf("%d %d\n",L+1,R);
    }
}

=============================今天就快速進入正題,code已附上
他基本上用來解的題目是這樣子的-Q:給一個序列(有N個值),問長度大於等於K的最大平均和?

預先操作為將序列轉成sum[i]序列(就是由a0+...+ai),題目也轉成在座標平面上(x為i值,y為sum[i]值),選出兩點使(sum[i]-sum[j])/(i-j)的值最大(其中i-j+1>=K),也就是斜率最大(故稱為斜率優化)



naive作法:枚舉i並枚舉他的j,O(n^2),爛作法-3-



這時,你重新的看一下你想要做的事情:



你其實想要做的是對於所有的i,要從K限制以下的點中,找出一點,使i節點與那一個點的斜率最大。

而從幾何上看來,即為一個圍起來的凸多邊形,而在 此多邊形 外的點,要與其中的所有點有最大斜率or最小斜率時,其必出現在端點上(線性規劃的精隨)。



(pf: 一個不是端點的點,代表在一個距離為e的圓(或半圓)內必存在另一個也在此多邊形中的點,若此點有最大斜率,則在此點指向i的向量順時針轉180度的範圍內移動e,皆可使斜率變大(反之則斜率變小),故唯有某一端點才會有最大斜率)



這時你就得到一個較快速的方法,求出前面的凸包,一個一個枚舉,而你又可以知道上半部的凸包並不是最大的(反而是最小的),原因如同上面的證明,所以你只需要記錄下凸包即可,但複雜度仍為O(n^2)。(下凸包定義:斜率遞增,且不會有點在此函數下方)


再多想一下你想要找的是什麼:



你發現了其實你要找的是一個切點-定義為與i形成的直線『斜率最大』的點-,經過一些直覺的猜想與證明,你知道這個切點與i形成的直線斜率要介於這個切點與兩個邊所形成的直線斜率之間,把它做切塊后,你可以很清楚的看到這樣的點只有一個,除非是在邊上(如圖所示),而且每個點與i形成的斜率會呈現遞增再遞減的趨勢。



{性質:斜率會是遞增再遞減,而切點則是使與i形成的斜率介於兩個邊所形成的直線斜率之間,且唯一}

pf:先證遞減,同理可證遞增








pf:再證當斜率介於這個切點與兩個邊所形成的直線斜率之間,則斜率比兩邊都還要大

這時你就可以用一個二分搜的方法求出切點,演算法複雜度:O(nlgn),但這並不是最好的,因為再多看一下我放的第二張圖,你會發現當你做到某一切點時,前面的點都可以刪掉了,因為若之後的點會把前方的點當做切點時,他的斜率必介於兩個邊的斜率之間,而且邊的斜率遞增,所以這個斜率一定會小於現在做到的斜率,故你也不需要去看前面的點了。加上這個單調性優化,複雜度變成了O(n)。






















3 則留言:

  1. 話說 你電爆他 跟我一點關聯也沒有啊
    我只是提共錯誤想法而已xD
    你真的好罩><

    回覆刪除
    回覆
    1. 我本來都沒有打算學這東西耶-3-

      還不是先被你電了,才來學

      刪除
    2. 我也去把 blog 加入了這題,也有東施效顰畫了幾個圖,也嘗試用自己的方法證明,現在如果有人問我,我可以很清楚地講完了,這都要托你的福

      刪除