1. 程式人生 > 實用技巧 >斜率 優化 dp

斜率 優化 dp

\(\large斜率優化dp\)

//P2365
#include<cstdio>
#define ll long long
using namespace std;
ll f[5010],sumt[5010],sumc[5010];
int n,s;
ll min(int a,int b){return a<b?a:b;}
signed main(){
 int n,s,t,c;
    scanf("%d%d",&n,&s);
    for (int i=1;i<=n;++i){
        scanf("%d%d",&t,&c);
        sumt[i]=sumt[i-1]+t;
        sumc[i]=sumc[i-1]+c;
    }
    for (int i=1;i<=n;++i)
        f[i]=0x3f3f3f3f;
    for (int i=1;i<=n;++i)
    for (int j=0;j<=i-1;++j)
        f[i]=min(f[i],f[j]+sumc[i]*sumt[i]+s*sumc[n]-sumc[j]*(sumt[i]+s));
    printf("%d",f[n]);
    return 0;
}

\[luoguP2365\\ F[i]=min_{0\le j<i}\{F[j]+sumT[i]*(sumC[i]-sumC[j]+S*(sumC[N]-sumC[j])) \}O(n^2)\\ luoguP5785\\ 資料超級加倍\\ 對上式子進行優化,把常數,僅與i有關的項,僅與j有關的項以及i,j乘積項分離\\ F[i]=min_{0\le j<i}\{ F[j]-(S+sumT[i])*sumC[j]\}+sumT[i]*sumC[i]+S*sumC[N];\\ 去掉min,把關於j的看做變數F[j]和sumC[j],其餘部分看做常量\\ 得到F[j]=(S+sumT[i])*sumC[j]+F[i]-sumT[i]*sumC[i]-S*sumC[N];\\ 以sumC[j]為橫座標,F[j]為縱座標,建立座標軸,進行線性規劃\\ 斜率: sumT(i)+S\\ 縱截距: f(i)−sumT(i)×sumC(i)−S×sumC(n)\\ 我們可以發現縱截距越大,F[i]越大\\ 通俗一點講,所謂選擇最優決策點就是把一條直線從下向上靠,第一個相交的點就是最優決策點(因為此時b最小,f_i也必定最小)。 \]

偷的Chpu437的圖 23333

取所有的點,尋找一個凸包(凸多邊形)使其圍住所有點在凸包上進行線性規劃

為了探究最佳決策需要滿足的條件, 我們設三個決策為\(j_1,j_2,j_3,且j_1<j_2<j_3\)

從圖可以發現,\(j_2\)有可能成為最優結構,當且僅當:

\[\frac{F[j_2]-F[j_1]}{sumC[j_2]-sumC[j_1]}<\frac{F[j_3]-F[j_2]}{sumC[j_3]-sumC[j_2]} \]

在下凸殼,斜率單調遞增,下凸殼的頂點才可能成為最優決策,對於某條斜率為k的直線,某頂點左側線段斜率比k小,

右側線段比k大,頂點為最優決策,直線斜率滿足單調

同時, 由於 sumC 單調遞增, 新決策的橫座標一定大於舊決策, 又因為 sumT 單調遞增, S+sumT(i), 即 kl 單調遞增, 如果我們對於凸殼上每兩個相鄰頂點的截距, 只保留大於 kl 的部分, 那麼最優決策一定在這個凸殼的左端點上.

所以我們建立一個單調佇列,若斜率\((F[q[l+1]]-F[q[l]])/(sumC[q[l+1]]-sumC[q[l]])\le S+sumT[i]\),則把\(q[l]\)出隊

luogu Stay_Hungry 大佬的圖

複雜度為O(n)

程式碼先咕會 咕咕咕

對於斜率為負數時,要找下凸點,維護下凸包