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);
    }
}

 

 
posted @ 2018-02-19 14:55  TRTTG  阅读(276)  评论(0编辑  收藏  举报