吃掉 N 个橘子的最少天数

吃掉 N 个橘子的最少天数

CategoryDifficultyLikesDislikes
algorithmsHard (26.35%)96-

Tags

Unknown

Companies

Unknown

厨房里总共有 n 个橘子,你决定每一天选择如下方式之一吃这些橘子:

  • 吃掉一个橘子。
  • 如果剩余橘子数 n 能被 2 整除,那么你可以吃掉 n/2 个橘子。
  • 如果剩余橘子数 n 能被 3 整除,那么你可以吃掉 2*(n/3) 个橘子。

每天你只能从以上 3 种方案中选择一种方案。

请你返回吃掉所有 n 个橘子的最少天数。

示例 1:

1
2
3
4
5
6
7
8
输入:n = 10
输出:4
解释:你总共有 10 个橘子。
第 1 天:吃 1 个橘子,剩余橘子数 10 - 1 = 9。
第 2 天:吃 6 个橘子,剩余橘子数 9 - 2*(9/3) = 9 - 6 = 3。(9 可以被 3 整除)
第 3 天:吃 2 个橘子,剩余橘子数 3 - 2*(3/3) = 3 - 2 = 1。
第 4 天:吃掉最后 1 个橘子,剩余橘子数 1 - 1 = 0。
你需要至少 4 天吃掉 10 个橘子。

示例 2:

1
2
3
4
5
6
7
输入:n = 6
输出:3
解释:你总共有 6 个橘子。
第 1 天:吃 3 个橘子,剩余橘子数 6 - 6/2 = 6 - 3 = 3。(6 可以被 2 整除)
第 2 天:吃 2 个橘子,剩余橘子数 3 - 2*(3/3) = 3 - 2 = 1。(3 可以被 3 整除)
第 3 天:吃掉剩余 1 个橘子,剩余橘子数 1 - 1 = 0。
你至少需要 3 天吃掉 6 个橘子。

示例 3:

1
2
输入:n = 1
输出:1

示例 4:

1
2
输入:n = 56
输出:6

提示:

  • 1 <= n <= 2*10^9

Discussion | Solution

解法

  • 该问题明显是一个递归问题,先选择一种吃法,剩下的成为递归子问题;

  • 吃法:

    • 0,1,2这三个分别是0,1,2;

    • 当大于2个的时候,每次吃一个显然是最慢的,

    • 每次最好选择吃1/2或者2/3,选择的依据是剩下的加上当前步骤应该为最小的;

    • 每种选择k必须先将不整除的先一个一个吃掉, 需消耗n%k天;

    • 因此总天数: f(n) = min(n%2+1+f(n/2), n%3+1+f(n/3))

代码

解法一:自顶向下递归

通过HashMap剪支,加快结果,时间复杂度$O(log(n))$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::collections::HashMap;
use std::cmp;

impl Solution {
    pub fn min_days(n: i32) -> i32 {
        let mut record = HashMap::new();  //记录已经吃过的方法

        Self::eat(n, &mut record)
    }

    pub fn eat(n: i32, record: &mut HashMap<i32, i32>) -> i32 {
        match n {
            0 | 1 => n,
            i if record.contains_key(&i) => *record.get(&i).unwrap(),
            _ => {
                let res = cmp::min(Self::eat(n/2, record) + n%2, Self::eat(n/3, record) + n%3) + 1;
                record.insert(n, res);
                res
            }
        }
    }
}

解法二:自底向上

通过数组缓存所有结果,时间复杂度:$O(n)$, leetcode超时;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    pub fn min_days(n: i32) -> i32 {
        if n < 2 {
            return n;
        }
        let mut res: Vec<i32> = vec![0 , 1];
        for i in 2..n+1 {
            res.push(cmp::min(res[(i/2) as usize] + i%2, res[(i/3) as usize] + i%3) + 1);
        }

        return res[n as usize];
    }
updatedupdated2024-08-252024-08-25