1. 程式人生 > >[BZOJ 1911] 特別行動隊

[BZOJ 1911] 特別行動隊

最優決策 判斷 col .cn a* cout www 凸包 大小

Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1911

Algorithm:

DP方程:dp[i]=max(dp[j]+a*(sum[i]-sum[j])^2+b*(sum[i]-sum[j])+c)

方程是顯然的,但復雜度為O(N^2),需要優化到O(N),這時就需要斜率優化了

推薦博客:https://www.cnblogs.com/MashiroSky/p/6009685.html

這篇博客清晰地從“數”到“形”展現了斜率優化

其中必要的前提:dp數組是要具有決策單調性的(即如果在dp[i]下決策j好於k,那麽在大於i時j永遠要好於k)

同時與i相關的數組具有單調性(不具有決定性,可以通過凸包二分來解決非單調性問題

此題符合這幾個前提,

如果j>k且j比k更優 :(由決策單調性推出)

dp[j]-dp[k]+a*sum[j]^2-a*sum[k]^2+b*(sum[k]-sum[j])>2*a*(sum[j]-sum[k])*sum[i]

SLOPE(j,k)=(dp[j]-dp[k]+a*sum[j]^2-a*sum[k]^2+b*(sum[k]-sum[j])) / (2*a*(sum[j]-sum[k]))>sum[i]

用單調隊列維護SLOPE

1、使SLOPE(cur,cur+1)保持遞增,因為sum[i]遞增

2、由於SLOPE(j,k) > sum[i] 時決策j比k更優,因此決策head>head+1>……tail

每次將SLOPE(head,head+1) < sum[i]的head踢出隊列,之後的queue[head]即為最優決策

記住,SLOPE只是用於判斷在sum[i]時j比k優的結論是否成立,我們用優先隊列來優化維護其的復雜度

SLOPE(j,k)和SLOPE(j‘‘ , k‘‘)間的大小關系與決策的優越性不具有直接聯系

Code:

#include <bits/stdc++.h>

using namespace std;
typedef 
long long ll; const int MAXN=1e6+10; ll pre[MAXN],dp[MAXN],que[MAXN],l,r,a,b,c,n; inline ll read() { char ch;ll num,f=0; while(!isdigit(ch=getchar())) f|=(ch==-); num=ch-0; while(isdigit(ch=getchar())) num=num*10+ch-0; return f?-num:num; } ll sqr(ll x){return x*x;} inline double slope(int x,int y) { return (double)(dp[x]-dp[y]+a*sqr(pre[x])-a*sqr(pre[y])+b*(pre[y]-pre[x]))/(double)(2*a*(pre[x]-pre[y])); } int main() { n=read();a=read();b=read();c=read(); for(int i=1;i<=n;i++) pre[i]=read(),pre[i]+=pre[i-1]; for(int i=1;i<=n;i++) { while(l<r && slope(que[l],que[l+1])<=pre[i]) l++; dp[i]=dp[que[l]]+a*sqr(pre[i]-pre[que[l]])+b*(pre[i]-pre[que[l]])+c; while(l<r && slope(que[r],i)<=slope(que[r-1],que[r])) r--; que[++r]=i; } cout << dp[n]; return 0; }

Review:

1、如果轉移方程顯而易見,但要優化復雜度

只要其具有決策單調性,且可由其推出的式子發現與i相關的量可看成斜率 / 轉移方程中的dp[i]可看作截距

均可使用斜率優化

2、當與i相關的數組不具有單調性時,要利用二分/三分法找到對應斜率(Updating)

[BZOJ 1911] 特別行動隊