DESCRIPTION
题意:
有存款上限不超过k元钱,若取钱大于剩余,atm会报错,小于就会取走一部分,问在不超过w次报错下,取出所有钱的最小期望。
这道题一开始想到的最小期望,是用二分答案来逼近自己的存款,若报错次数等于w次就gg,即结果为0.我们只需要枚举0-k这样来枚举存款,在对每次进行二分答案,每次的初始概率就是1/k+1。
但是二分并不是最小期望,只能说“老练”“稳妥”“机智”.但是不得不承认我随机选一个可能一次就取走全部储蓄,并通过w次机会知道自己已经取完了,这样更快(注意atm机并不会告诉你当前余额为多少,也就是说就算你取完了也不定知道自己取完了,这是个坑点).
在此,我们用dp二维数组来表示,第一维i表示我知道了我的存款最大可能是多少(根据自己前面选择的经验),第二维是j还能有j次报错机会.
转移方程里面我们枚举本次取走k元钱,我们知道余额上界为i(i也在枚举哦),那我们有(i-k+1)/(i+1)的几率取得比存款少,即(存款>=k),注意可能存款是零,所以有i+1种情况.那剩下的概率是报错的概率,你不会拿走任何钱,并且报警机会也少了一次(详见代码).
备注:
1.dp存的是次数
2.期望是指: 出现这种结果的概率*结果
3.结果在不同的题可能不一样,比如本题为取完钱的次数.
4.本题注意一开始存款上界数据给的是2000,之前说过二分是很笨的办法,所以log2000是15左右(其实更少,比如13),所以机会>15的直接用15的结果即可——最小期望嘛.
5.感谢w1419的讲解
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=2050;
const int inf=210000000;
double dp[N][17];
int k,w;
int main(){
for(int i=0;i<N;i++) dp[i][0]=inf;//一次机会都没有
for(int i=0;i<17;i++) dp[0][i]=0;//你知道最大是0,你就不需要操作了
for(int i=1;i<N;i++)
for(int j=1;j<17;j++){
dp[i][j]=inf;
for(int k=1;k<=i;k++)
dp[i][j]=min(dp[i][j],(i-k+1.0)/(i+1)*dp[i-k][j]+k/(i+1.0)*dp[k-1][j-1]+1); +1为本次操作次数
}
while(~scanf("%d%d",&k,&w)){
w=min(w,13);
printf("%0.6lf\n",dp[k][w]);
}
}