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

一、题目

1、题目描述

你需要访问 n 个房间,房间从 0 到 n - 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];
    }
};
相关推荐
企业数字化笔记7 小时前
视频人体姿态怎么做三维可视化?2D关键点、深度局限与技术选型
人工智能·算法·音视频
?abc!8 小时前
(数组)581. 最短无序连续子数组
数据结构·算法·leetcode
小郎君65810 小时前
MPTT树复选框的半选状态:一棵百万节点树从递归到计数器,再到区间覆盖
算法
酌沧10 小时前
MemT让记忆成为模型内生能力
jvm·人工智能·算法
hold?fish:palm10 小时前
40 二叉树的直径
c++·leetcode
Shimeri11 小时前
越卷越像:我用遗传算法做了个"内卷"实验
算法·three.js·数据可视化·遗传算法·进化算法
Evand J11 小时前
【PMSM例程1】永磁同步电机(PMSM)转速与转子电角度观测算法|基于扩展卡尔曼滤波(EKF)的传感器状态估计与误差。附下载链接
算法·电机·1024程序员节·卡尔曼滤波
guo_wen_qiang11 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节
爱读书的小胖12 小时前
LeetCode 55 跳跃游戏 C 语言解题分析
c语言·数据结构·算法·leetcode·1024程序员节
CCblandy12 小时前
前缀缓存命中率不靠引擎靠排列:本机仿真 3 轮,易变字段放开头 98.0%→0.0%
人工智能·python·算法·自然语言处理·性能优化