记忆化搜索&动态规划,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个房间 + 从奇数次访问nextVisit[n - 1]到第二次访问第n - 1个房间 + 1 + 1

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

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

那么f(i) = f(i - 1) * 2 + f(nextVisit[i - 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];
    }
};
相关推荐
CoovallyAIHub20 分钟前
为什么85%的企业AI项目都失败了?
深度学习·算法·计算机视觉
KarrySmile23 分钟前
Day8--滑动窗口与双指针--1004. 最大连续1的个数 III,1658. 将 x 减到 0 的最小操作数,3641. 最长半重复子数组
数据结构·算法·双指针·滑动窗口·不定长滑动窗口·最大连续1的个数·最长子数组
zc.ovo26 分钟前
图论水题4
c++·算法·图论
KyollBM32 分钟前
【Luogu】每日一题——Day20. P4366 [Code+#4] 最短路 (图论)
算法·图论
qqxhb33 分钟前
零基础数据结构与算法——第七章:算法实践与工程应用-金融算法
算法·风险评估算法·金融算法·交易策略算法·欺诈检测算法
墩墩同学1 小时前
【LeetCode题解】LeetCode 74. 搜索二维矩阵
算法·leetcode·二分查找
SunnyKriSmile1 小时前
输入10个数并求最大值
c语言·算法
汤永红2 小时前
week2-[循环嵌套]数位和为m倍数的数
c++·算法·信睡奥赛
1白天的黑夜14 小时前
前缀和-560.和为k的子数组-力扣(LeetCode)
c++·leetcode·前缀和
m0_672813774 小时前
Leetcode-3427变长子数组求和
leetcode