LeetCode 1553 吃掉N个橘子的最少天数

题目链接:LeetCode 1553 吃掉N个橘子的最少天数

题目大意:

题解:
三条结论:

  1. 在任意一次操作\(2\)之前最多只会有\(1\)次操作\(1\)
  2. 在任意一次操作\(3\)之前最多只会有\(2\)次操作\(1\)
  3. 除了最后的一次操作\(1\)之外,其余连续的操作\(1\)之后都会有操作\(2\)\(3\)

证明参考自LeedCode官方题解

记忆化搜索

通过记忆化搜索实现上述三条结论:

  • 对于任意的橘子数\(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;
    }
};
posted @ 2022-02-08 18:35  ZZHHOOUU  阅读(67)  评论(0编辑  收藏  举报