ZCMU 1894: Power Eggs
http://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=1894
题意:
有M个鹰蛋,N层楼,鹰蛋的硬度是E,也就是说在1~E层楼扔下去不会碎,E+1层楼扔下去会碎。
给定M,N,问最坏情况下至少几次能得到E的具体的值。(E可能为0)
①n<=100。
②n<=1000。
③n<=100000。
④n<=1000000。
⑤n<=1000000000。
推荐学习资料: 朱晨光IOI2004集训队论文 从《鹰蛋》一题浅析对动态规划算法的优化
算法一: O(n^3)
dp[i][j] 表示用i个蛋在j层楼上确定E,最坏情况下的最少次数
枚举在第w层扔下1个蛋,要么碎,要么不碎
碎:用剩下的i-1个蛋在下面的w-1层里确定E
不碎:剩下的i个蛋在 w+1~n 层里确定E,这相当于在 下面的n-w 层确定E
所以dp[i][j]=min{ max(dp[i-1][w-1],dp[i][j-w]) } +1
事件复杂度为 O(n^2 * m),m认为与n同阶
算法二:O(n^2 * logn)
根据判定树的理论,长为n的有序线性表,最坏查找需要次数为logn [上取整]
所以当鹰蛋的个数超过 log(n+1) [上取整] 时,直接输出log(n+1) [上取整]
算法三:O(n*logn*logn)
比较显然的结论:dp[i][j]>=dp[i][j-1]
(严谨的证明去看论文)
dp[i][j]=min{ max(dp[i-1][w-1],dp[i][j-w]) } +1
dp[i-1][w-1] 随w的增大单调不减
dp[i][j-w] 随w的增大单调不增
对于每一个w,对应的这两条线谁在上面就取谁
所以最终更新 dp[i][j]的w是这两条线的交点
可以二分找这个w
算法四:O(n*logn)
dp[i][j]=min{ max(dp[i-1][w-1],dp[i][j-w]) } +1
对于任意的w,满足 dp[i][j]<= max(dp[i-1][w-1],dp[i][j-w]) +1
令w=1,那么dp[i][j]<= max(dp[i-1][0],dp[i][j-1]) +1
所以dp[i][j]<=dp[i][j-1]+1
所以 dp[i][j-1]<=dp[i][j]<=dp[i][j-1]+1
所以若存在一个w,能够使得dp[i][j]=dp[i][j-1],则dp[i][j]=dp[i][j-1]
若对于所有的w,都不能使得dp[i][j]=dp[i][j-1],则dp[i][j]=dp[i][j-1]+1
令p满足 dp[i][p]=dp[i][j-1]-1,dp[i][p+1]=dp[i][j-1]
那么dp[i][p]=dp[i][j-1]-1
dp[i][p+1]=dp[i][p+2]=……=dp[i][j-1]
计算dp[i][j]时,令j-w=p,则w=j-p
则 dp[i][j]=min{ max(dp[i-1][j-p-1],dp[i][p]) }
可以证明
当 dp[i-1][j-p-1]<=dp[i][p] 时,dp[i][j]=dp[i-1][j]
当 dp[i-1][j-p-1]>dp[i][p] 时,dp[i][j]=dp[i-1][j]+1
具体证明去看论文
算法五:
dp[i][j] 表示 用i个蛋,扔j次最坏情况下最大能确定的楼层数
扔一次碎了,那么剩下j-1次,剩下i-1个蛋
我们也希望用剩下的次数和剩下的蛋在下面能确定的楼层数最大,所以是dp[i-1][j-1]
扔一次没碎,那么剩下j-1次,剩下i个蛋
我们也希望用剩下的次数和剩下的蛋在上面能确定的楼层数最大,所以是dp[i][j-1]
加上扔蛋的这一次
所以 dp[i][j]=dp[i-1][j-1]+dp[i][j-1]+1
如果只有一个蛋,只能1层1层的试,dp[1][i]=i
如果只有一层,dp[i][1]=1
初始化和转移都跟 组合数C 很像
C爆炸式增长,所以这个也是爆炸式增长
论文里有证明
也就是说当n很大的时候,i和j很小
当n=2e9时,i和j只取到32就A了
#include<cstdio> using namespace std; typedef long long LL; LL dp[33][33]; void DP() { for(int i=1;i<=32;++i) dp[i][1]=1,dp[1][i]=i; for(int i=2;i<=32;++i) for(int j=2;j<=32;++j) dp[i][j]=dp[i][j-1]+dp[i-1][j-1]+1; } int main() { DP(); int T; scanf("%d",&T); int n,m,ans; while(T--) { scanf("%d%d",&n,&m); ans=-1; for(int i=1;i<=32;++i) if(dp[m][i]>=n) { ans=i; break; } if(ans==-1) puts("Impossible"); else printf("%d\n",ans); } }