LeetCode 1553 吃掉N个橘子的最少天数
题目链接:LeetCode 1553 吃掉N个橘子的最少天数
题目大意:
题解:
三条结论:
- 在任意一次操作\(2\)之前最多只会有\(1\)次操作\(1\);
- 在任意一次操作\(3\)之前最多只会有\(2\)次操作\(1\);
- 除了最后的一次操作\(1\)之外,其余连续的操作\(1\)之后都会有操作\(2\)或\(3\)。
记忆化搜索
通过记忆化搜索实现上述三条结论:
- 对于任意的橘子数\(i \geq 2\),将\(n\)通过操作\(1\)减少到最近的\(2\)的倍数,随后进行一次操作\(2\)。写成递推式即为:
\[f(i) = i \% 2 + 1 + f(\lfloor i/2 \rfloor)
\]
- 对于任意的橘子数\(i \geq 3\),将\(n\)通过操作\(1\)减少到最近的\(3\)的倍数,随后进行一次操作\(3\)。写成递推式即为:
\[f(i) = i \% 3 + 1 + f(\lfloor i/3 \rfloor)
\]
- 除了最后的一次操作\(1\)之外,其余连续的操作\(1\)之后都会有操作\(2\)或\(3\)。即:
\[f(1) = 1
\]
class Solution {
private:
unordered_map<int, int> hashTable;
public:
int minDays(int n) {
if (n <= 1) {
return n;
}
if (hashTable.count(n)) {
return hashTable[n];
}
return hashTable[n] = min(n % 2 + 1 + minDays(n / 2), n % 3 + 1 + minDays(n / 3));
}
};
最短路
我们也可以将思路抽象成一个最短路问题,即:
- 图\(G\)中有若干个节点,每个节点表示着一个数。根据上述方法,每个节点对应着一个\(\lfloor n/(2^x3^y) \rfloor\),节点数为\(O(\log^2 n)\);
- 图\(G\)中有若干条有向边,如果某个节点表示的数为\(i\),那么\(i\)到\(\lfloor i/2 \rfloor\)有一条长度为\(i\%2 + 1\)的有向边,\(i\)到\(\lfloor i/3 \rfloor\)有一条长度为\(i\%3 + 1\)的有向边。边数同样为\(O(\log^2 n)\);
- 我们需要求出\(n\)对应的节点到\(1\)对应的节点的最短路径的长度。
因此我们可以用\(Dijkstra\)算法求出答案。
using PII = pair<int, int>;
class Solution {
public:
int minDays(int n) {
priority_queue<PII, vector<PII>, greater<PII>> q;
unordered_set<int> visited;
q.emplace(0, n);
int ans = 0;
while (!q.empty()) {
auto [days, node] = q.top();
q.pop();
if (visited.count(node)) {
continue;
}
visited.insert(node);
if (node == 1) {
ans = days + 1;
break;
}
q.emplace(days + 1 + node % 2, node / 2);
q.emplace(days + 1 + node % 3, node / 3);
}
return ans;
}
};