题意:银行里有[x,y]的钱,你选择取i块钱,取钱成功花费手续费a,并取钱;取钱失败花费手续费b,无别的操作。你要将里面所有钱都取出来,而且得确信里面没钱。问最少手续费?
题解:
所谓确信里面没钱,有两种情况,假设当前已知区间
[
x
,
y
]
[x,y]
[x,y](x 不为0),一种就是取y元成功,结束;另一种就是取x+1元失败,那么下一步就是取x元成功,结束。
[
x
,
y
]
[x,y]
[x,y](x 为0)时,第一种相同,第二种为取1元失败,结束。
显而易见,
[
x
,
y
]
[x,y]
[x,y](x不为0)元可转化为
[
1
,
y
−
x
+
1
]
[1,y-x+1]
[1,y−x+1]的情况。
n
2
n^2
n2复杂度的dp式子显而易见不难推,分成取成功取失败两种情况,并对上面情况进行讨论:
设
d
p
[
0
,
x
]
dp[0,x]
dp[0,x]表示最多有为x元,左端点为0,
d
p
[
1
,
x
]
dp[1,x]
dp[1,x]表示最多有x,左端点不为0。
d
p
[
0
,
x
]
=
m
i
n
(
m
a
x
(
d
p
[
0
,
i
−
1
]
+
b
,
d
p
[
0
,
x
−
i
]
+
a
)
)
(
i
∈
[
1
,
x
]
)
;
dp[0,x]=min(max(dp[0,i-1]+b,dp[0,x-i]+a))(i \in[1,x]);
dp[0,x]=min(max(dp[0,i−1]+b,dp[0,x−i]+a))(i∈[1,x]);
d
p
[
1
,
x
]
=
m
i
n
(
m
a
x
(
d
p
[
1
,
i
−
1
]
+
b
,
d
p
[
0
,
x
−
i
]
+
a
)
)
(
i
∈
[
1
,
x
]
)
;
dp[1,x]=min(max(dp[1,i-1]+b,dp[0,x-i]+a))(i \in[1,x]);
dp[1,x]=min(max(dp[1,i−1]+b,dp[0,x−i]+a))(i∈[1,x]);
初始值
d
p
[
0
,
0
]
=
0
,
d
p
[
1
,
0
]
=
a
dp[0,0]=0,dp[1,0]=a
dp[0,0]=0,dp[1,0]=a
对于这个,出题人题解是有二分做法,函数三分。因为其中有单调性,我利用单调性写的是线性的。因为从 x x x位变换到 x + 1 x+1 x+1位,最大值位置变换不会很大。(为什么?凭感觉,,要证太麻烦,,懒得补这坑,,不过其实我本来打算写个小小的退火,没想到直接过了)因此从上一个位置的最大值往两边搜索即可。
---------------------仔细一想,回来补一下单调性的证明(真繁琐呢)
只要证了
d
p
[
0
,
x
]
dp[0,x]
dp[0,x]的就可以,
d
p
[
1
,
x
]
dp[1,x]
dp[1,x]的同理。用反证法来证吧。
对于
d
p
[
0
,
x
+
1
]
,
d
p
[
0
,
x
]
dp[0,x+1],dp[0,x]
dp[0,x+1],dp[0,x],假设
d
p
[
0
,
x
]
dp[0,x]
dp[0,x]的最大值位置为
m
i
mi
mi,即
d
p
[
0
,
x
]
=
m
a
x
(
d
p
[
0
,
m
i
−
1
]
+
b
,
d
p
[
0
,
x
−
m
i
]
+
a
)
dp[0,x]=max(dp[0,mi-1]+b,dp[0,x-mi]+a)
dp[0,x]=max(dp[0,mi−1]+b,dp[0,x−mi]+a),那么分类讨论
d
p
[
0
,
x
+
1
]
dp[0,x+1]
dp[0,x+1]:
假设最大值同为 m i mi mi,
-
那么 d p [ 0 , x + 1 ] = m a x ( d p [ 0 , m i − 1 ] + b , d p [ 0 , x + 1 − m i ] + a ) dp[0,x+1]=max(dp[0,mi-1]+b,dp[0,x+1-mi]+a) dp[0,x+1]=max(dp[0,mi−1]+b,dp[0,x+1−mi]+a),
当 d p [ 0 , x + 1 ] = d p [ 0 , m i − 1 ] + b dp[0,x+1]=dp[0,mi-1]+b dp[0,x+1]=dp[0,mi−1]+b时,显然 d p [ 0 , x ] dp[0,x] dp[0,x]也为这个值,因此单调不上升; -
当 d p [ 0 , x + 1 ] = d p [ 0 , x + 1 − m i ] + a dp[0,x+1]=dp[0,x+1-mi]+a dp[0,x+1]=dp[0,x+1−mi]+a时,
假设 d p [ 0 , x ] = d p [ 0 , m i − 1 ] + b dp[0,x]=dp[0,mi-1]+b dp[0,x]=dp[0,mi−1]+b,已知 d p [ 0 , x + 1 ] = m a x ( d p [ 0 , x + 1 − m i ] + a , d p [ 0 , m i − 1 ] + b ) = d p [ 0 , x + 1 − m i ] + a > d p [ 0 , m i − 1 ] + b = d p [ 0 , x ] dp[0,x+1]=max(dp[0,x+1-mi]+a,dp[0,mi-1]+b)=dp[0,x+1-mi]+a>dp[0,mi-1]+b=dp[0,x] dp[0,x+1]=max(dp[0,x+1−mi]+a,dp[0,mi−1]+b)=dp[0,x+1−mi]+a>dp[0,mi−1]+b=dp[0,x],此时会变大; -
假设 d p [ 0 , x ] = d p [ 0 , x − m i ] + a dp[0,x]=dp[0,x-mi]+a dp[0,x]=dp[0,x−mi]+a,此时 d p [ 0 , x + 1 ] − d p [ 0 , x ] = d p [ x + 1 − m i ] − d p [ x − m i ] dp[0,x+1]-dp[0,x]=dp[x+1-mi]-dp[x-mi] dp[0,x+1]−dp[0,x]=dp[x+1−mi]−dp[x−mi],最差时一直迭代到 d p [ 0 , 1 ] 和 d p [ 0 , 0 ] dp[0,1]和dp[0,0] dp[0,1]和dp[0,0],而已知 d p [ 0 , 1 ] > d p [ 0 , 0 ] dp[0,1]>dp[0,0] dp[0,1]>dp[0,0],所以这种情况仍会单调。
假设最大值不为 m i mi mi,设为 m j mj mj,
- 则
d
p
[
0
,
x
+
1
]
=
m
a
x
(
d
p
[
0
,
m
j
−
1
]
+
b
,
d
p
[
0
,
x
+
1
−
m
j
]
+
a
)
dp[0,x+1]=max(dp[0,mj-1]+b,dp[0,x+1-mj]+a)
dp[0,x+1]=max(dp[0,mj−1]+b,dp[0,x+1−mj]+a),
已知 d p [ 0 , x ] < = m a x ( d p [ 0 , m j − 1 ] + b , d p [ 0 , x − m j ] + a ) dp[0,x]<=max(dp[0,mj-1]+b,dp[0,x-mj]+a) dp[0,x]<=max(dp[0,mj−1]+b,dp[0,x−mj]+a),这个与上面式子比较,又回到上面的情况。
综上所述, d p [ 0 , x ] dp[0,x] dp[0,x]单调不上升性证毕。
代码:
#include<bits/stdc++.h>
#include<algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <vector>
#include <bitset>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((long long)(x).size())
#define mod Mod
const int maxn = 2e5 + 5;
int x,y,a,b,T;
ll dp[maxn],dp2[maxn];
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&x,&y,&a,&b);
int n = y - x;
dp[0] = 0;
int dan = 1;
rep(i,1,n){
dp[i] = max(dp[dan-1]+b,dp[i-dan]+a);
int l = dan - 1,r = dan + 1;
while(l){
ll dpp = max(dp[l-1]+b,dp[i-l]+a);
if(dpp < dp[i]){
dan = l;
dp[i] = dpp;
l --;
}
else break;
}
while(r<=i){
ll dpp = max(dp[r-1]+b,dp[i-r]+a);
if(dpp < dp[i]){
dan = r;
dp[i] = dpp;
r ++;
}
else break;
}
}
if(x == 0){
printf("%lld\n",dp[n]);
continue;
}
else{
dp2[0] = a;
dan = 1;
rep(i,1,n){
dp2[i] = max(dp2[dan-1]+b,dp[i-dan]+a);
int l = dan - 1,r = dan + 1;
while(l){
ll dpp = max(dp2[l-1]+b,dp[i-l]+a);
if(dpp < dp2[i]){
dan = l;
dp2[i] = dpp;
l --;
}
else break;
}
while(r<=i){
ll dpp = max(dp2[r-1]+b,dp[i-r]+a);
if(dpp < dp2[i]){
dan = r;
dp2[i] = dpp;
r ++;
}
else break;
}
}
printf("%lld\n",dp2[n]);
}
}
return 0;
}
``