题目描述
小沈阳在小品里说过:“人生最痛苦的事情是人死了,钱还没花掉”。 于是小宋(80 岁)决定要将所有的储蓄从 ATM 机中取出花光。 小宋忘记 了她有多少存款(银行卡密码她是记得的 2333),这个奇怪的ATM不支持查询 存款余额功能。小宋知道她存款的唯一信息是存款上限是K元,这意味着小宋的存款 x 是 0 到 K 之间的随机整数(包括 K)。 每次小宋都可以尝试从 ATM 中拿出一些钱。 如果她要取的 y 元钱不大于 她的存款,ATM 将立即给小宋y元。 但如果她的存款小于y,小宋将收到ATM的警告。如果小宋被警告超过w次,那么她将被警方带走,作为小偷。 小宋希望取钱次数期望最小。 由于小宋聪明,她总是采取最好的策略。 请计算小宋将所有储蓄从自动取款机中取出期望次数最小值是多少,并不得被警方带走。
输入格式
每个测试点包含多组测试数据(最多 10 组)每组测试数据包含两个整数 K,和 W 1≤K,W≤2000
输出格式
对于每组测试数据输出取钱次数最小的期望值,舍入到小数点后 6 位。
输入样例
1 1
4 2
20 3
输出样例
1.000000
2.400000
4.523810
分析
我以为所谓的最优策略仅仅是二分,没想到这个80岁的老奶奶不仅会二分,还会期望dp
本题的最好策略的意思就是每次的选择期望次数最小的方案进行决策(语文不好的悲哀
取完不单单是指他存折里没钱了,小宋需要得到确定的信息告诉他取完了。
我们是已知上限$K$元,最多可以被警告$W$次(反正只有这两个信息)
发现每次取钱都可以确定新的上限
考虑末状态,根据我们对"取完"的理解
末状态就是已知上限0元,最多可以被警告$w$次($0 \leq w \leq W$)
这样就可以设$dp[i][j]$表示已知上限i元,最多还可以被警告j次的期望次数
那么就有$dp[0][j]=0(0 \leq j \leq W)$
枚举每次取的个数v,就可以转移了。
$$dp[i][j]=min(\frac {v} {i+1} dp[v-1][j-1]+\frac {i+1-v} {i+1}dp[i-v][j]+1)$$
然后可以突然发现时间复杂度为$O(N^2 W)$。
但是,正如之前所说
这个80岁的老奶奶是会二分答案的!
所以,$W$最大取到11就足够了($2^{11}=2048>2000$)
Code
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;double f[2005][15];
int main()
{
for(int i=1;i<=2000;i++)for(int j=0;j<=11;j++)f[i][j]=1e10;
for(int i=1;i<=2000;i++)for(int j=1;j<=11;j++)for(int v=1;v<=i;v++)
f[i][j]=min(f[i][j],(v)/1.0/(i+1)*f[v-1][j-1]+(i+1-v)/1.0/(i+1)*f[i-v][j]+1.0);
while(scanf("%d%d",&n,&m)==2)
{
m=min(11,m);
printf("%.6lf\n",f[n][m]);
}
}