# 單調隊列 ## [a146. Sliding Window](https://zerojudge.tw/ShowProblem?problemid=a146) ## [Nearest Smaller Values](https://cses.fi/problemset/task/1645) ## [Advertisement](https://cses.fi/problemset/task/1142) ## 有限個數的多重背包問題 # 斜率優化 ## [Monster Game I](https://cses.fi/problemset/task/2084) $$ dp[i]=\min_{j<i} \{ f_j\times s_i+dp[j]\} $$ ### 觀察1 很像直線$y=ax+b$的形式 所以我們可以維護一個直線的集合,每次詢問找將$s_i$帶入這些直線的最小值。 詢問後加入集合 ### 觀察2  對於所有的$j<i$,我們只需要存黑色的就好。 剛好會是一個凸包 ### 觀察3 計算答案  因為詢問單調,如果詢問是藍色,那麼斜率比轉移點大的都可以刪掉了。 ### 觀察4  將直線加入凸包後,可能會有一些斜率小且**連續**的直線被淘汰。 ### 實作 維護一個直線的deque,斜率遞減。 詢問時: 重複比較斜率最**大**的兩條直線$L_1$、$L_2$,如果$L_2$的答案比$L_1$還要好的話就可以將$L_1$丟掉,反之$L_1$就會是轉移點。 加入時: 對於斜率最**小**的兩條直線$L_1$、$L_2$及加入的直線$L$,有兩種情形:  若$L$、$L_2$的交點$C$的$x$座標小於$L_1$、$L_2$的交點$D$的$x$座標,則可以把$L_1$丟掉。(這一題斜率都是正的,所以也可以看$y$座標) 即 $$ \begin{cases} C_y=L_{a}C_x+L_b \\C_y=L_{2_a}C_x+L_{2_b} \\D_y=L_{1_a}D_x+L_{1_b} \\D_y=L_{2_a}D_x+L_{2_b} \end{cases} $$ 中,判斷是否有$C_x<D_x$,由上式可知 $$ \begin{cases} C_x=\frac{L_b-L_{2_b}}{L_{2_a}-L_a} \\D_x=\frac{L_{1_b}-L_{2_b}}{L_{2_a}-L_{1_a}} \end{cases} $$ 為了避免浮點數誤差,交叉相乘,所以要檢查 $$ (L_b-L_{2_b})(L_{2_a}-L_{1_a})\leq (L_{2_a}-L_a)(L_{1_b}-L_{2_b}) $$ 是否成立 ### code ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; struct F{ int a,b; F(int _a,int _b):a(_a),b(_b){} int operator()(int x){ return a*x+b; } }; bool check(F l1,F l2,F l){ return (l.b-l2.b)*(l2.a-l1.a)<=(l2.a-l.a)*(l1.b-l2.b); } signed main(){ int n,x; cin>>n>>x; vector<int> s(n+1),f(n+1),dp(n+1,1e18); for(int i=1;i<=n;i++) cin>>s[i]; for(int i=1;i<=n;i++) cin>>f[i]; dp[0]=0; f[0]=x; deque<F> dq; dq.push_back(F(x,0)); for(int i=1;i<=n;i++){ while(dq.size()>=2&&dq[1](s[i])<=dq[0](s[i])){ dq.pop_front(); } dp[i]=dq[0](s[i]); F l(f[i],dp[i]); while(dq.size()>=2&&check(dq.back(),dq[dq.size()-2],l)){ dq.pop_back(); } dq.push_back(l); } cout<<dp[n]<<endl; } ``` ## [Monster Game II](https://cses.fi/problemset/task/2085) 查詢和斜率都不單調的版本 李超線段樹 查詢不單調: 對每個直線存他是最佳答案的區間,二分搜 斜率不單調: 動態凸包
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.