1. 程式人生 > >【筆記篇】斜率優化dp(二) SDOI2016征途

【筆記篇】斜率優化dp(二) SDOI2016征途

不能 最小化 征途 這樣的 string cpp mar logs -s

=======傳=送=門=======
搜題目名會搜出很多奇怪的東西... 這個題目似乎有點毒?
比如在bzoj和loj上可以1A的代碼上會在luogu TLE 2個點, 在cogs TLE 10個點 但是根據已有的資料來看數據都是一樣的...毒瘤評測姬毀我OI!!!
這個題的狀態轉移方程並不是很好推的說. 出題人讓\(*m^2\)肯定是有目的的啊..
(比如不讓乘\(m^2\)我們可能會需要考慮乘\(m^2\)最後再除掉之類的)

然後就化一波式子: 我們令\(sum\)表示\(n\)段路的總和.
\[ m^2s^2=m^2\frac{\sum_{i=1}^m(x_i-\bar x)^2}m=m\sum_{i=1}^m(x_i-\bar x)^2\\=m\sum_{i=1}^mx_i^2-m\sum_{i=1}^m2x_i\bar x+m\sum_{i=1}^m\bar x^2\=m\sum_{i=1}^mx_i^2-(2\sum_{i=1}^mx_i)*(m*\bar x)+m^2(\frac{sum}m)^2\\=m\sum_{i=1}^mx_i^2-2sum^2+sum^2=m\sum_{i=1}^mx_i^2-sum^2 \]


\(m\)\(sum^2\)都是常數我們可以不管, 那就是要求最小化\(\sum_{i=1}^mx_i^2\).
所以令\(f[i][j]\)表示前\(i\)天走了前\(j\)段路, \(s_i\)表示前\(i\)段路的前綴和, 那就能寫出狀態轉移方程:
\[ f[i][j]=min\{f[i-1][k]+(s_j-s_k)^2\} (k\in[1,j)) \]
那很明顯這個是\(O(n^3)\)可以做的, 這樣能拿到60pts了就.
但是想A的話 很明顯要采用一種\(o(n^2)\)的算法. 當然你要能\(O(n)\)甚至\(O(1)\)過也沒啥問題...
那我們就要搬出斜率優化了. 我們繼續化式子.
首先很明顯第一維跟後面這一堆沒啥關系, 那就不優化了, 也可以把這一維去掉, 到時候一滾動數組(其實不滾也能過)就行了.
那狀態轉移方程就可以改寫成:
\[ f[j]=min\{f'[k]+(s_j-s_k)^2\} \]

然後繼續化成y=kx+b的形式, \[f[j]=f'[k]+s_j^2-2s_js_k+s_k^2\]
移項得\(f'[k]+s_k^2\)=\(2s_j\)\(s_k+\)\(f[j]-s_j^2\)
這樣的話我們就可以正常的斜率優化了. 最後輸出\(m*f[n][m]-sum^2\)就好啦~

不過要修一下邊界條件.

  • 比如第\(i\)天完全可以從\(i\)
    開始找, 總不可能回去找前面的路(這樣也不會出現被0除錯誤),
  • 然後\(f[1][x]\)顯然應該等於\(s[x]^2\), 這樣就可以了.
  • 然後又是要開long long的題整天開long long還是挺煩的, 什麽時候普及64位系統啊= =

然後就是代碼: 並不知道究竟能不能AC 請謹慎復制!

#include <cstdio>
#include <cstring>
const int N=3030;typedef long long LL;
LL s[N],q[N],n,m,h,t;LL f[N],g[N];
inline LL gn(LL a=0,char c=0){
    for(;c<'0'||c>'9';c=getchar());
    for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
inline double slope(LL x,LL y){return 1.0*(g[x]+s[x]*s[x]-g[y]-s[y]*s[y])/(s[x]-s[y]);}
int main(){ n=gn(); m=gn();
    for(LL i=1;i<=n;++i) s[i]=s[i-1]+gn(),g[i]=s[i]*s[i];
    for(LL i=2;i<=m;++i){h=0; t=0; q[h]=i-1;
        for(LL j=i;j<=n;++j){    
            while(h<t&&slope(q[h],q[h+1])<2*s[j]) ++h;
            f[j]=g[q[h]]+(s[j]-s[q[h]])*(s[j]-s[q[h]]);
            while(h<t&&slope(q[t],q[t-1])>slope(j,q[t])) --t;
            q[++t]=j;
        }::memcpy(g,f,sizeof(g));
    }printf("%lld",f[n]*m-s[n]*s[n]);
}

被莫名的非主觀因素的TLE卡掉好多下午的學(tui)(fei)時間, 心情並不怎麽好...
不過下雪了出去玩了一圈就非常爽了~ (⊙v⊙)嗯

【筆記篇】斜率優化dp(二) SDOI2016征途