題目鏈接:點我啊╭(╯^╰)╮
題目大意:
你的銀行賬戶餘額在 範圍內
你需要把它全部取出來,若取
若當前餘額 ,則耗費 元取出
若當前餘額 ,則耗費 元,取出失敗
問全取出最壞情況下的最低耗費
解題思路:
假設 ,那麼很明顯是二分
每次取中點,這樣一定最貪心
問題在於 ,那麼中點就不一定是最貪心的
所以要枚舉中間每一個點
發現答案與區間長度 有關,與 無關
但是發現 時,當我們取到 時,無論成功失敗, 都不需要再取
如果餘額在 ,當我們取到 時,如果失敗,還要取
所以分兩種情況DP:
表示 左端點爲 ,長度爲 的答案, 則表示左端點不爲
枚舉 ,若 失敗,則範圍變爲
若 成功,則變爲
這樣的 是 的,很明顯看出來區間長度越長,答案越大
也就是 單調不減,那麼 裏的函數就是一個單調不增,一個單調不減
所以 ,就是先減後增
所以對與某一個 ,設其決策點爲
易得對於 的點,其決策點 只會往後移
均攤時間複雜度:
核心:DP + 單調優化
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int T, x, y, a, b;
ll dp[maxn][2];
ll slope0(int j, int i){
return max(dp[j-1][0] + b, dp[i-j][0] + a);
}
ll slope1(int k, int i){
return max(dp[k-1][1] + b, dp[i-k][0] + a);
}
int main() {
scanf("%d", &T);
while(T--){
scanf("%d%d%d%d", &x, &y, &a, &b);
int n = y - x;
dp[0][0] = 0, dp[0][1] = a;
for(int i=1, j=1, k=1; i<=n; i++){
while(j<i && slope0(j+1, i) <= slope0(j, i)) j++;
while(k<i && slope1(k+1, i) <= slope1(k, i)) k++;
dp[i][0] = slope0(j, i), dp[i][1] = slope1(k, i);
}
printf("%lld\n", dp[n][x>0]);
}
}