HDU 5781 ATM Mechine(dp)

202 篇文章 1 订阅

Description
知道银行存款余额上限k(余额可能是0~k中任一个),尝试取钱,对于一个尝试金额,如果余额够则能取出,否则警告一次,警告超过w次就会被警察抓走,问取出所有钱的尝试次数的最小期望
Input
多组用例,每组用例输入两个整数k和w表示存款余额上限以及警告次数上限
(1<=k,w<=2000)
Output
对于每组用例,输出取出所有钱的尝试次数的最小期望,结果保留到小数点后六位
Sample Input
1 1
4 2
20 3
Sample Output
1.000000
2.400000
4.523810
Solution
令dp[i][j]为余额上限为i,剩余尝试次数为j时取出所有钱的最小尝试次数之和,那么答案即为dp[k][w]/(k+1),根据枚举当前尝试取钱数k得到最优解的思路即可得到一下转移方程
dp[i][j]=max(dp[i-k][j]+dp[k-1][j-1]+i+1)(k=1,2,…i)
右边三部分意义分别是:
1.尝试取k元成功,那么余额上限变成i-k,尝试次数不变为j
2.尝试取k元被警告,那么余额上限变成k-1,尝试次数减一变成j-1
3.因为余额可能是0~i中任意一个金额,每一种都需要尝试取k元,所以总尝试次数要加上i+1
这个dp是O(w*k^2)的,也就是O(8*10^9)的复杂度,显然不能接受,但是注意到w太大就相当于取消了警告这个限制(类似二分的策略也能够在log k,也就是大约11步内取完所有钱),故只要限制下w的大小(例如在w和10中取较小值),那么时间复杂度就降到了O(10*k^2),也就是O(4*10^7),这个是可以接受的
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 2222
int k,w,dp[maxn][16];
int main()
{
    for(int i=0;i<=2000;i++)dp[0][i]=0;
    for(int i=1;i<=2000;i++)
        for(int j=0;j<=10;j++)
            dp[i][j]=INF;   
    for(int i=1;i<=2000;i++)
        for(int j=1;j<=10;j++)
            for(int k=1;k<=i;k++)
                dp[i][j]=min(dp[i][j],dp[i-k][j]+dp[k-1][j-1]+i+1);
    while(~scanf("%d%d",&k,&w))
    {
        w=min(10,w);
        printf("%.6f\n",1.0*dp[k][w]/(k+1));
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值