说明:仅仅分享思想和代码设计方法,代码是用于验证和对照(对照是为了方便你检查你写的哪里有问题),我强烈建议你自己动手实现,我明确反对你直接用我的代码去写报告或其它目的。
参考了网上的教程http://blog.sina.com.cn/s/blog_6c813dbd0101bh98.html
原问题:为100楼,两个鸡蛋测试多少次,可以找到恰好鸡蛋碎的楼的问题。做个简单的转换,假设k次测试,两个鸡蛋最多能测试n楼。则问题求解就变为k为何值时,n大于100。
这个问题复杂度为O(1),因为指定了鸡蛋数量。因为对于n楼,你可以第一次扔在k楼,
此时如果碎了,你剩下的k-1测试和你的一个鸡蛋一楼一楼试,能找出结果。如果没碎,你还有两个鸡蛋,加k-1次机会,你第二次鸡蛋扔在k+k-1层,此时若碎了,你剩下的k-2测试和你的一个鸡蛋一楼一楼试,能找到结果。递推发现n=k+k-1+k-2+...1。所以就一个数学表达式的复杂度O(1).
证明:可证明采用这种方法两个鸡蛋能测试最多的楼层。采用数学归纳法。对于k=1次测试机会这显然成立。假设k=n,这种方法正确。令k=n+1。则第一次机会必然放在楼层n+1以下。假设放在n+1以上,则加入第一次鸡蛋碎了,剩下只能一楼一楼测了,那还剩余n次机会,就不能保证一定能找到原来会碎的楼层了。
拓展:所以为使用动态规划知识,原问题可更改为n个鸡蛋,k次测试,最多测试多少楼的问题。
参考上面的想法,得到递归表达式,这个想法由上面衍生,对于n个鸡蛋,k次机会,你可以第一次放在n-1个鸡蛋,k-1次机会的上方,如果蛋碎了,你能找到碎的楼层,如果没碎,则原问题转化为n个鸡蛋,k-1机会所测的高度加上你现在的高度。
然后递归式收敛情况,分两种,一个是你机会用完了,
一个是测得只剩下只有一只鸡蛋了,。
分析:如果这个递归表达式n,k均不指定,那时间复杂度渐进确界为(n方),指定一个则为n,都指定为1。
回到原问题如果给定楼层高度,和鸡蛋数n。那么k得从小一个一个试出来。相当于k未指定,时间复杂度升了一个量级为n。(无论情况的好坏)
谈回指明鸡蛋数的情况:
空间复杂度,如果使用动态规划算法,则需要一个鸡蛋数*楼层高度得表,目的是为了不计算相同子问题。空间复杂度n方。
如果单纯使用递归,主要是递归工作栈的开销。
int egg(int n, int k)
{
if (n == 1)
return k;
if (k == 0)
return 0;
return egg(n - 1, k - 1) + egg(n, k - 1) + 1;
}
int eggs(int n, int k)
{
if (n == 1&&k!=0)
{
if (find(sss.begin(), sss.end(), k) == sss.end())
{
sss.push_back(k);
a += k + 1;
if (a < 50)
cout << a << ' ';
}
return k;
}
if (k == 0)
return 0;
return eggs(n - 1, k - 1) + eggs(n, k - 1) + 1;
}
int main()
{
int i, j;
for(int i=1;i<8;++i)
for (int leg = 50; leg <= 200; leg *= 2)
{
for ( j = 2; j <= 3; ++j)
{
cout << "楼数为" << leg << "鸡蛋数为" << j<<"的情况:" ;
for (i = 1; egg(j, i) < leg; ++i);
cout << "需要至少" << i << "次测试" << endl;
}
}
cout << "当鸡蛋数为2,楼数为50的详细放蛋过程(假设蛋均未碎)为:" << endl;
cout << "楼层:";
eggs(2, 10);
cout << endl;
a = 0;
}