【题解】牛油果-NOI2019模拟赛 by Wearry

NOI2019模拟赛 T1
by Wearry

题目描述

牛油果是一种神秘的水果,其具有一个坚固程度 x ≥ 0 x ≥ 0 x0:即从高度不超过 x x x 米的地方掉下来都不会受损,否则就会破碎。现在有 n n n 个牛油果可以用来做实验,如果某个牛油果在一次实验的过程破碎了就不能继续做实验,否则就可以继续拿来做实验。假设给出的牛油果坚固程度相同,且已知它们的坚固程度不超过 m m m,现在要求最坏情况下最少做多少次实验可以得知它们的坚固程度。多组数据。

数据规模:

对于 10 % 10\% 10% 的数据, n , m , T ≤ 10 n,m,T\le 10 n,m,T10
对于 30 % 30\% 30% 的数据, n , m , T ≤ 50 n,m,T\le 50 n,m,T50
对于 60 % 60\% 60% 的数据, n , m , T ≤ 500 n,m,T\le 500 n,m,T500
对于 100 % 100\% 100% 的数据, n , m , T ≤ 5000 n,m,T\le 5000 n,m,T5000

题解

签到题。

考试时花了半个小时才想到 60 60 60 分做法。

考虑 dp.

f [ i ] [ j ] f[i][j] f[i][j] 为有 i i i 个牛油果,坚固程度小于等于 j j j 时,最坏情况下最少做实验的次数。

分类讨论,若在高度为 k k k 的位置扔下牛油果烂或者不烂。

若在高度为 k k k 的位置扔下牛油果烂了,那么答案为 f [ i − 1 ] [ k − 1 ] + 1 f[i-1][k-1]+1 f[i1][k1]+1,因为剩下的 i − 1 i-1 i1 个牛油果,而坚固程度 x x x 必定小于 k k k,也就是小于等于 k − 1 k-1 k1.

若在高度为 k k k 的位置扔下牛油果没烂,那么答案为 f [ i ] [ j − k ] + 1 f[i][j-k]+1 f[i][jk]+1,因为还有 i i i 个牛油果,而范围变成了 k ≤ x ≤ j k\le x\le j kxj,范围大小为 j − k j-k jk.

所以得 f [ i ] [ j ] = min ⁡ k = 1 j ( max ⁡ ( f [ i − 1 ] [ k − 1 ] , f [ i ] [ j − k ] ) + 1 ) f[i][j]=\min\limits_{k=1}^{j}(\max(f[i-1][k-1],f[i][j-k])+1) f[i][j]=k=1minj(max(f[i1][k1],f[i][jk])+1).

这样的时间复杂度为 O ( n m 2 ) O(nm^2) O(nm2). 这是我考试时的做法,可得 60 60 60 分。

其实当 n n n 大于 14 14 14 时可以当成 14 14 14 算。

若不考虑 n n n 的限制,最多只需要扔 log ⁡ m \log m logm 次牛油果就能得出结果。

所以当 n n n 大于 log ⁡ 5000 ≤ 14 \log 5000\le 14 log500014 的时候,我们只需要最多 14 14 14 个牛油果就能得出答案。

那么 f [ i ] [ j ] f[i][j] f[i][j] i i i 只用枚举到 14 14 14 就可以了。

时间复杂度 O ( m 2 log ⁡ m ) O(m^2\log m) O(m2logm).

话说好像直接输出 log ⁡ m \log m logm 能过。czn 和 szt 使用 log ⁡ m \log m logm 乱搞通过了本题,并且复杂度爆踩标程。样例是用哪只脚造的啊。我太菜了,只会 60 60 60 分 dp.

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5005;
int T, n, m, f[N][N];
void init() {
    memset(f, 0x3f, sizeof(f));
    for (int i = 1; i < N; i++) f[i][0] = 0, f[i][1] = 1, f[1][i] = i;
    for (int i = 2; i <= 14; i++)
        for (int j = 1; j < N; j++)
            for (int k = 1; k <= j; k++)
                f[i][j] = min(f[i][j], max(f[i - 1][k - 1], f[i][j - k]) + 1);
}
int main() {
    init();
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m), n = min(n, 14);
        printf("%d\n", f[n][m]);
    }
    return 0;
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值