class Solution:
def minDays(self, n: int) -> int:
from functools import lru_cache
@lru_cache(None)
def dp(x):
if x == 0:
return 0
if x == 1:
return 1
# Option 1: Eat one orange
# Option 2: Eat x//2 oranges if x % 2 == 0
# Option 3: Eat 2*x//3 oranges if x % 3 == 0
# For option 2 and 3, we may need to eat some oranges one by one to reach a divisible number
return 1 + min(
x % 2 + dp(x // 2),
x % 3 + dp(x // 3)
)
return dp(n)
#include <unordered_map>
class Solution {
public:
std::unordered_map<int, int> memo;
int minDays(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
if (memo.count(n)) return memo[n];
int eatOne = 1 + minDays(n - 1);
int eatHalf = 1 + (n % 2) + minDays(n / 2);
int eatTwoThirds = 1 + (n % 3) + minDays(n / 3);
int res = std::min(eatHalf, eatTwoThirds);
memo[n] = std::min(eatOne, res);
return memo[n];
}
};
import java.util.*;
class Solution {
private Map<Integer, Integer> memo = new HashMap<>();
public int minDays(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
if (memo.containsKey(n)) return memo.get(n);
int eatOne = 1 + minDays(n - 1);
int eatHalf = 1 + (n % 2) + minDays(n / 2);
int eatTwoThirds = 1 + (n % 3) + minDays(n / 3);
int res = Math.min(eatHalf, eatTwoThirds);
int ans = Math.min(eatOne, res);
memo.put(n, ans);
return ans;
}
}
var minDays = function(n) {
const memo = new Map();
function dp(x) {
if (x === 0) return 0;
if (x === 1) return 1;
if (memo.has(x)) return memo.get(x);
let eatHalf = 1 + (x % 2) + dp(Math.floor(x / 2));
let eatTwoThirds = 1 + (x % 3) + dp(Math.floor(x / 3));
let res = Math.min(eatHalf, eatTwoThirds);
memo.set(x, res);
return res;
}
return dp(n);
};
Given an integer n
representing the number of oranges you have, you want to eat all of them in the minimum number of days. Each day, you can choose one of the following actions:
n
is divisible by 2, eat n / 2
oranges in one day.n
is divisible by 3, eat 2 * (n / 3)
oranges in one day (i.e., leave n / 3
oranges).
Find the minimum number of days required to eat all n
oranges. Each orange can only be eaten once, and you must choose one action per day.
At first glance, the problem seems to ask for a simple greedy solution: always take the largest possible chunk (either by 2 or by 3) if possible. However, this does not always yield the fewest days, because sometimes eating one orange to reach a divisible state can be optimal.
Brute-force recursion tries all possible choices at each step, but quickly becomes infeasible for large n
due to repeated subproblems. To optimize, we realize that the problem has overlapping subproblems and optimal substructure, making it a perfect candidate for memoization (dynamic programming).
The key is to minimize the number of days by considering, at each state:
n
by 1.n/2
oranges if n
is divisible by 2, possibly after eating a few one by one to reach divisibility.2*(n/3)
oranges if n
is divisible by 3, possibly after eating a few one by one to reach divisibility.n
in a hash map, we avoid redundant calculations.
We solve the problem using recursion with memoization (top-down dynamic programming). The main idea is to define a function dp(n)
that returns the minimum number of days to eat n
oranges.
n == 0
, return 0 (no oranges left).n == 1
, return 1 (one day to eat the last orange).1 + dp(n - 1)
.n
is divisible by 2, eat half. But if not, eat n % 2
one by one until it is, then eat half: n % 2 + 1 + dp(n // 2)
.n
is divisible by 3, eat two thirds. If not, eat n % 3
one by one until it is, then eat two thirds: n % 3 + 1 + dp(n // 3)
.n
to avoid recomputation.n
, return the minimum of the three options above.
This approach ensures we always find the optimal (minimum days) path, and by caching results, we vastly reduce the time complexity.
Let's walk through the example where n = 10
:
n = 10
n = 5
n = 5
is:
n = 10
, Option 2: 1 (eat half) + 4 (minDays(5)) = 5 days
n = 10
, the minimum is 4 days.
This example shows how the recursive approach explores all possibilities and always takes the path with the fewest days, even if it means eating one orange at a time to reach a better chunk.
n
.
n
is computed only once and stored. Since each recursive call reduces n
by at least a fraction (by 2 or 3), the number of unique states is at most O(n)
.
O(1)
work (arithmetic and hash map lookup), so total time is O(n)
.
O(n)
for the memoization table and recursion stack.
The problem asks for the minimum number of days to eat all n
oranges, where each day you can eat one orange, half (if divisible by 2), or two-thirds (if divisible by 3). A naive greedy approach does not always yield the best result, so we use recursion with memoization to try all possible choices at each step, caching results for efficiency. This dynamic programming solution ensures optimality and drastically reduces computation time, making it elegant and efficient for large inputs.