记忆化搜索&动态规划,LeetCode 1997. 访问完所有房间的第一天

一、题目

1、题目描述

你需要访问 n 个房间,房间从 0n - 1 编号。同时,每一天都有一个日期编号,从 0 开始,依天数递增。你每天都会访问一个房间。

最开始的第 0 天,你访问 0 号房间。给你一个长度为 n下标从 0 开始 的数组 nextVisit 。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:

  • 假设某一天,你访问 i 号房间。
  • 如果算上本次访问,访问 i 号房间的次数为 奇数 ,那么 第二天 需要访问 nextVisit[i] 所指定的房间,其中 0 <= nextVisit[i] <= i
  • 如果算上本次访问,访问 i 号房间的次数为 偶数 ,那么 第二天 需要访问 (i + 1) mod n 号房间。

请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对 109 + 7 取余后的结果。

2、接口描述

​python3
python 复制代码
class Solution:
    def firstDayBeenInAllRooms(self, nextVisit: List[int]) -> int:
cpp
cpp 复制代码
class Solution {
public:
    int firstDayBeenInAllRooms(vector<int>& nextVisit) {

    }
};

3、原题链接

1997. 访问完所有房间的第一天


二、解题报告

1、思路分析

要求什么?

访问完所有房间的第一天

由于根据题目要求,第一次访问第n个房间的前提是第n - 1个房间被访问两次,访问第n - 1个房间两次的前提是第n - 2个房间访问4次......

因而访问完所有房间的第一天就是第一次访问第n个房间的天数

和子问题的关系?

第一次访问第n个房间 <=> 第二次访问第n - 1个房间 + 1

<=> 第一次访问第n - 1个房间 + 从奇数次访问nextVisitn - 1到第二次访问第n - 1个房间 + 1 + 1

而从奇数次访问nextVisitn - 1到第二次访问第n - 1个房间等价为第一次访问第n - 1个房间的天数减去第一次访问第nextVisitn - 1个房间

设f(i)为第一次访问i号房间的天数

那么f(i) = f(i - 1) * 2 + f(nextVisiti - 1) + 2

我们可以用记忆化搜索来实现,也可以递推实现

2、复杂度

时间复杂度: O(n) 空间复杂度:O(n)

3、代码详解

​F1 记忆化搜索

python3

python 复制代码
class Solution:
    def firstDayBeenInAllRooms(self, nextVisit: List[int]) -> int:
        n, mod = len(nextVisit), 1000000007
        @cache
        def f(x: int) -> int:
            if not x:
                return 0
            return (2 + f(x - 1) * 2 - f(nextVisit[x - 1]) + mod) % mod
        return f(n - 1)

cpp

cpp 复制代码
class Solution {
public:
    int mem[100005];
    static constexpr int mod = 1e9 + 7;
    int f(int x, vector<int>& nextVisit){
        if(~mem[x]) return mem[x];
        return mem[x] = (2 + 2LL * f(x - 1, nextVisit) - f(nextVisit[x - 1], nextVisit) + mod) % mod;
    }
    int firstDayBeenInAllRooms(vector<int>& nextVisit) {
        int n = nextVisit.size();
        memset(mem, -1, sizeof mem);
        mem[0] = 0;
        return f(n - 1, nextVisit);
    }
};
F2 递推

python

python 复制代码
class Solution:
    def firstDayBeenInAllRooms(self, nextVisit: List[int]) -> int:
        n, mod = len(nextVisit), 1_000_000_007
        f = [0] * n
        for i in range(1, n):
            f[i] = (2 + f[i - 1] * 2 - f[nextVisit[i - 1]] + mod) % mod
        return f[n - 1]

cpp

cpp 复制代码
class Solution {
public:
static constexpr int mod = 1'000'000'007;
    int firstDayBeenInAllRooms(vector<int>& nextVisit) {
        int n = nextVisit.size();
        vector<int> f(n, 0);
        for(int i = 1; i < n; i++)
            f[i] = (2LL + 2LL * f[i - 1] - f[nextVisit[i - 1]] + mod) % mod;
        return f[n - 1];
    }
};
相关推荐
Jerry5 小时前
LeetCode 160. 相交链表
算法
Jerry5 小时前
LeetCode 19. 删除链表的倒数第 N 个结点
算法
金銀銅鐵5 小时前
费马小定理
python·数学·算法
技术不好的崎鸣同学7 小时前
[ACTF2020 新生赛]Exec 思路及解法
算法·安全·web安全
Full Stack Developme8 小时前
Java LRU 与 LFU 算法及应用
java·开发语言·算法
Jerry9 小时前
LeetCode 707. 设计链表
算法
C语言小火车10 小时前
C++ 堆排序深度精讲:基于完全二叉树的选择排序进化,最坏情况 O(n log n) 的稳定王者
开发语言·c++·算法·排序算法·堆排序
weixin_4000056010 小时前
Vision-Language-Action:LMDrive双损失函数训练模块与 LangAuto 基准评测框架
人工智能·深度学习·算法·机器学习·自动驾驶
kebidaixu10 小时前
两轮BMS AFE SH367306 I2C 读写时序
算法