大楼扔鸡蛋问题(动态规划)

题目链接:poj 3783

题意分析:
经典题,紫书上的一道例题,4+2出了这道原题,我愣是以为是数学题,最后也没做出来。题意是这样的,给你N个鸡蛋(硬度一样),让你测鸡蛋的硬度,测量的方法就是从某栋M层的楼的某一层X上把鸡蛋扔下来,如果鸡蛋碎了,代表他的强度小于X;如果没碎,则强度大于等于X。我们要做的就是不断的从楼上把鸡蛋扔下来,直到找到某一层楼X,从这一层楼扔下来鸡蛋不碎掉,从X+1层扔下来鸡蛋碎掉,那么鸡蛋的强度就是X。如果在M层扔下来鸡蛋也不碎掉,那么鸡蛋的强度为M。问题是,用N个鸡蛋最多需要几步可以把硬度测出来(鸡蛋碎掉就不能用了,而且必须测出来!)。
其实真正的问题是,对于硬度取值范围为[0, M]的鸡蛋,N次蛋碎的机会,最多需要多少步一定可以测出鸡蛋的硬度。

解题思路:
一开始看懂题意之后,我马上就想到了二分法,但是仔细一想并不对,如果鸡蛋的数量足够的话,二分法绝对是最快的,但是如果鸡蛋不够,就不太一样了。如果第一次扔就碎了一个鸡蛋,那么剩下的鸡蛋就只能从下往上测试,这样2个鸡蛋、100层楼的情况最多可能需要51步,然而测试样例告诉我们,只要14步,当时想了足足有半个钟才想到他可以用{ 14,13,12 …… }这样的步长进行测试,这样最多的测试数都是14次,这样的原则确确实实可以解决2个鸡蛋的问题,但是3个鸡蛋就跪了,于是有了动态规划算法。
动态规划算法:
状态:
dp[i][j]表示N=i,M=j时,最多需要多少次测试一定可以测出鸡蛋的硬度。
状态转移方程:
如果我们一开始是在k层进行测试的,那么如果鸡蛋破碎了,我们的查找范围就变成k层以下的k-1层,当然此时鸡蛋数减少了,所以最终的步数应该为dp[i-1][k-1]+1;另外一种情况是鸡蛋没有碎的情况,我们要找的范围变成了k层以上的,所以最终需要dp[i][j-k]+1步。我们的目标就是要找到一个k,使得最坏情况下测试数最少,所以我们需要枚举k。代码如下:

    for(int i=2;i<=MAXN;i++)
    {
        for(int j=2;j<=MAXM;j++)
        {
            for(int k=1;kmin(dp[i][j],max(dp[i-1][k-1]+1,dp[i][j-k]+1));
            }
        }
    }

初始状态:
N=1时,测试数为M;M=1,测试数为1;M=0,测试数为0;

AC代码:

#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define MAXN 55
#define MAXM 1005

using namespace std;

int n,m,dp[55][1005];

int main()
{
    int T,t;
    scanf("%d",&T);

    memset(dp,INF,sizeof(dp));
    for(int i=1;i<=MAXM;i++)
        dp[1][i]=i;
    for(int i=1;i<=MAXN;i++)
        dp[i][1]=1,dp[i][0]=0;

    for(int i=2;i<=MAXN;i++)
    {
        for(int j=2;j<=MAXM;j++)
        {
            for(int k=1;k1][k-1]+1,dp[i][j-k]+1));
            }
        }
    }   

    while(T--)
    {
        scanf("%d %d %d",&t,&n,&m);
        printf("%d %d\n",t,dp[n][m]);
    }

    return 0;
}

总结:
其实如果知道是用动态规划做的话,状态什么的还是挺好想的,但是关键是想不到啊~~经验不足真可怕,不过被时间复杂度吓到也是一部分原因,居然没想到离线。
1、多坐点题积累经验吧;
2、不要被复杂度吓到;
3、事实证明,智商很重要TAT

你可能感兴趣的:(算法,动态规划,POJ,经典题目)