zoukankan      html  css  js  c++  java
  • 高楼扔鸡蛋问题(鹰蛋问题) POJ-3783

    这是一道经典的DP模板题。

    https://vjudge.net/problem/POJ-3783#author=Herlo

    一开始也是不知道咋写,尝试找了很多博客,感觉有点领悟之后写下自己的理解。

    题意就是:
    对于一种蛋,如果它在第m层楼摔不碎,但是在第m+1层摔碎了,那么它的硬度就是m。
    有若干层楼,你有若干个一样的蛋,你每一次尝试的结果都会是最坏的情况,问你在这种运气最差的情况下至少丢几次蛋才能知道它的硬度。

    A. 如果蛋的数量是0,那根本都尝试不了,只能试0次

    B. 如果你只有一个蛋,那你不敢随便丢吧?如果现在有100层你随便挑第50层一丢,它碎了,那你怎么确定它的硬度是多少?那么就只能从一楼开始一层一层往上试,直到它摔碎了,那么我们也确定了蛋的硬度。

    C. 如果你有两个蛋,那么你可以先尝试着在第k层丢一个蛋

    1. 如果蛋碎了,那么你手中只剩下一颗蛋能试了,这个时候你也知道,从第k+1层往上的层你也不用再考虑了,接下来剩下的一个蛋就在第1到第k-1层做尝试就好了。
    2. 如果它没碎,那么你手中还剩下两颗完好的蛋,并且,这个时候k层下面的1到k-1层,你都不用考虑在这些层丢蛋了,肯定不会碎,那就只需要考虑从第k+1层开始到顶层,你该怎样去尝试,来测出这个蛋的硬度了。

    看到上面两种情况,第一种情况,蛋的数量减小成为1了,并且待测的楼的层数也减少了,本质上变成了一个原问题的子问题,那么这个时候,我们可以看到当蛋只有一个,并且待测的楼层是k-1层时,这个答案肯定就是k-1啦,(只有一个蛋嘛)

    第二种情况,蛋还是2个,不过待测的楼层数也减少了,这个也是原问题的子问题,我们可以看到因为扔一个蛋只可能出现碎与不碎两种情况,并且需要尝试的次数是由前面我们说到的这两种子问题的情况再加上1次得来的,那么因为题目说是最坏的情况,所以这两个子问题,自然是哪个次数大,就选哪个来加上1来推出规模更大的子问题。

    D. 如果你有不止两个蛋,情况也是和上面说的一样,需要由子问题来推导得到,dp的本质也就是从子问题的最优解得到更大规模的子问题的最优解嘛。

    我们的dp数组有两个维度,第一个是蛋的数量,第二个是待测的楼层数,

    那么少不了两重循环来填表咯。

    外层i就是蛋的数量,内层j就是待测的楼层数

    E. 不过,这里还有一个问题,就是你在解决某一个子问题,假设是填dp【i】【j】这个格子的时候,如上文所说,你首先要在第k层先试扔一个吧?但这个k你怎么确定呢?这个时候,因为题目要求的是最小的扔的次数,所以最暴力的方法当然就是对于所有可能的k都遍历一遍啦,然后取一个最小的值来填我们的dp【i】【j】咯。

    所以既然这样,我们先将所有dp都设置为INF(无限大),因为要取一个最小值来填dp【i】【j】嘛,然后,上文A、B中的情况是不需要推出来的,是直接可以得到的结果,也就是我们的初始化条件。

    然后就是我们的三重循环填dp啦,

    递推式在代码中,结合上述文字仔细品味一下,应该不太难理解。

    此外,对于上文E这句话中的k值,好像还有更加优化的方法去做?(比如二分吗?)留待以后更新

    题解代码

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int INF = 0x3f3f3f3f;
    int cnt = 0,N;
    const int maxn = 10005;
    int T,id,B,M;
    int dp[55][1005];
    int main(){
    	#ifndef ONLINE_JUDGE
    		freopen("in.txt","r",stdin);
    	#endif
    	scanf("%d",&T);
    	while(T--){
    		scanf("%d%d%d",&id,&B,&M);
    		for(int i=1;i<=B;i++){
    			for(int j=1;j<=M;j++)
    				dp[i][j] = INF;
    		for(int i=0;i<=B;i++) dp[i][1] = 1, dp[i][0] = 0;
    		for(int i=0;i<=M;i++) dp[1][i] = i;
    		for(int i=2;i<=B;i++){
    			for(int j=2;j<=M;j++){
    				for(int k=1;k<=j;k++) 
    				dp[i][j] = min(dp[i][j],1+max(dp[i-1][k-1],dp[i][j-k]));
    			}
    		}
    		cout<<id<<" "<<dp[B][M]<<endl; 
    	}
    	return 0;
    }
    
  • 相关阅读:
    git Permissions 0777 for '/home/xxx/.ssh/id_rsa' are too open.
    Linux wc指令解析
    RK3288 开机动画旋转
    tp3.2 事务
    PHP实现动态获取函数参数的方法
    laravel 配置设置
    swoole 使用异步redis的前置条件
    php回调函数的概念及实例
    swoole 安装
    linux 源码安装PHP
  • 原文地址:https://www.cnblogs.com/kevin-matrix/p/14615480.html
Copyright © 2011-2022 走看看