HDU 5781 ATM-Mechine

353 阅读2分钟

【问题描述】

​ Alice打算从自动取款机上取出她的所有存款。但是她忘了自己有多少存款了,她只知道存款不超过k块钱。 ​ 但是这台取款机很奇怪,它不支持余额查询功能,Alice只能通过多次尝试的方式取钱。每次尝试,Alice输入一个提取金额y,若账户余额>=y,取款机会立即吐出y块钱。若余额<y,取款机会发出警告。如果取款机发出了w次警告,它会认为Alice在故意捣乱,就会立即把卡吞掉。

请你帮忙计算,在不被吞卡的情况下,期望多少次就能取出所有的钱?

【输入格式】

有5组数据

对于每组数据,只有一行, 两个整数 k和w.

【输出格式】

五行,每行对应一组数据的结果,只有一个数字,表示问题的答案,保留6个小数位。

【数据范围】

对于100%的数据, 1≤K,W≤2000

这真是一个神级取款机

题解

对于可能剩余 i​ 元钱, k​ 次错误的状态,取钱 j​ 的范围是 [0-i]​,共有 i+1​ 种可能

而取 j 元钱无非就两种结果:

①取到了 \to 那么剩下的可能的钱就是 i-j ,而剩余错误的次数仍然为 k

也就是说 dp[i][k]\to dp[i-j][k]

②没取到 \to 剩下的可能钱数就可能为 j-1(因为 i 一定存在不合法的取钱情况),错误次数也就变成了 k-1

也就是 dp[i][k]\to dp[j-1][k-1]

所以说我们枚举合法剩余金钱 j

那么合法的取钱的情况就是取 [0,i-j] 元钱,概率是 \frac{i-j+1}{i+1}

反之则是选择取 [i-j+1,i] ,概率是 \frac{j}{i+1}

也就是说 dp[i][k]=min_{k=1}^{n}dp[i-j][k]\cdot \frac{i-j+1}{i+1}+dp[j-1][k-1]\cdot \frac{j}{i+1}+1

这是一个反向的递推,+1就是表示每一步往后推都要走一步,取 min 是为了选择最优策略

有一个细节就是,对于每一次选择,我们即使是使用二分的策略,也能保证错误的次数在 log_2n 级别,所以错误的次数上线就是 log_{2}2000=15

附上超级短的代码

#include <iostream>
#include <cstdio>
using namespace std;

double dp[2345][17];

void DP()
{
	for(int i=1;i<=2000;i++)
	for(int j=0;j<=16;j++)dp[i][j]=10000;
	
	for(int i=1;i<=16;i++)dp[0][i]=0;
	for(int i=1;i<=2000;i++)
	for(int t=1;t<=16;t++)
	for(int j=1;j<=i;j++)
		dp[i][t]=min(dp[i][t],dp[i-j][t]*(i-j+1)/(i+1)+dp[j-1][t-1]*j/(i+1)+1);
}

int main()
{
	freopen("atm.in","r",stdin);
	freopen("atm.out","w",stdout);
	int T=5,m,t;
	DP();
	while(T--)
	{
		scanf("%d%d",&m,&t);
		t=min(t,15);
		printf("%.6lf\n",dp[m][t]);
	}
}