【自主训练B:dp递推】B :Dropping water balloons

自主训练B:B 题:Dropping water balloons

【难度】

4 / 10 4/10 4/10
多理解一下吧

【题意】

楼高 N N N
你有 K K K 个相同的水球。
水球有一个相同的抗击打系数,表现为:
在某一特定楼层以下扔球都不会破高于其特定楼层一定会破
当然,球破掉了就不能用了。
问你:最多试几次,就可以知道水球的抗击打系数?

如果尝试次数超过63次,输出“More than 63 trials needed.”

【数据范围】

1 ≤ k ≤ 100 1\le k\le 100 1k100
1 ≤ n ≤ 2 64 1\le n\le 2^{64} 1n264,反正是 i n t 64 int64 int64范围内的。

(Time limit 3000 ms)
多组样例,0 0结束

【输入样例】

2 100
10   \, 786599
4 786599
60 1844674407370955161
63 9223372036854775807
0 0

【输出样例】

14
21
More than 63 trials needed.
61
63

【思路】

关于双蛋问题,我们有 O ( K × N 2 ) O(K\times N^2) O(K×N2)的DP转移,不过这题 N N N 非常大。
那么我们考虑其他状态:
N N N 当做状态答案,把测试次数 a n s ans ans 和 可用水球数 K K K当做状态。

定义如下:
d p [   i   ] [   j   ] 表 示 测 试 了 i 次 , 水 球 最 多 破 j 次 , 最 多 能 判 断 出 多 大 范 围 内 的 答 案 dp[\,i\,][\,j\,] 表示\pmb{测试了 i 次,水球最多破j次},最多能判断出多大范围内的答案 dp[i][j]ij测试了i次,水球最多破jij
【注意:】
j j j 表示水球最多破 j j j 次,而不是已经破了 j j j 次,否则递推状态有误。

考虑状态转移方程:

若当前为 d p [ i ] [ j ] dp[i][j] dp[i][j] ,是从测试了 i − 1 i-1 i1次转移过来的。
若这次水球破了,那么 d p [ i ] [ j ] 包 括 了 d p [ i − 1 ] [ j − 1 ] dp[i][j]包括了dp[i-1][j-1] dp[i][j]dp[i1][j1]所能测出的范围
若这次水球没破,那么 d p [ i ] [ j ] 包 括 了 d p [ i − 1 ] [ j ] dp[i][j]包括了dp[i-1][j] dp[i][j]dp[i1][j]所能测出的范围
当然,你这次测试的那层楼已经被你测试过了,判断范围自然增加了这个1

那么状态转移方程为:

d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j ] + 1 dp[i][j]=dp[i-1][j-1]+dp[i-1][j]+1 dp[i][j]=dp[i1][j1]+dp[i1][j]+1

对于题目给定了的 K K K ,不断枚举答案,使得第一次满足 d p [ a n s ] [ k ] ≥ n \pmb{dp[ans][k] \ge n} dp[ans][k]ndp[ans][k]ndp[ans][k]n 即为答案。

【核心代码】

时间复杂度: O ( N × K + 64   T ) O(N\times K+64\,T) O(N×K+64T)
这题 N N N最大64, K K K 最大100,因此时间复杂度特别低。
Time(ms) 0
Mem(MB) 0
对的,时空复杂度都很低。

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 100+50;

ll dp[MAX][MAX];

int main()
{
    for(int i=1;i<=64;++i){
        for(int j=1;j<=100;++j){
            dp[i][j] = dp[i-1][j] + dp[i-1][j-1] + 1;
        }
    }
    int k;
    ll n;
    while(~scanf("%d%lld",&k,&n) && k && n){
        int ans = 64;
        for(int i=0;i<=63;++i){
            if(dp[i][k]>=n){
                ans = i;
                break;
            }
        }
        if(ans == 64)printf("More than 63 trials needed.\n");
        else printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(算法,动态规划)