LeetCode 1997. 访问完所有房间的第一天 思维动态规划
LeetCode 1997. 访问完所有房间的第一天
题目描述
你需要访问 n 个房间,房间从 0 到 n - 1 编号。同时,每一天都有一个日期编号,从 0 开始,依天数递增。你每天都会访问一个房间。
最开始的第 0 天,你访问 0 号房间。给你一个长度为 n 且 下标从 0 开始 的数组 nextVisit。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:
假设某一天,你访问 i 号房间。
如果算上本次访问,访问 i 号房间的次数为 奇数,那么 第二天 需要访问 nextVisit[i] 所指定的房间,其中 0 <= nextVisit[i] <= i。
如果算上本次访问,访问 i 号房间的次数为 偶数,那么 第二天 需要访问 (i + 1) mod n 号房间。
请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对 10^9 + 7 取余后的结果。
样例
输入:nextVisit = [0,0]
输出:2
解释:
- 第 0 天,你访问房间 0。访问 0 号房间的总次数为 1,次数为奇数。
下一天你需要访问房间的编号是 nextVisit[0] = 0 - 第 1 天,你访问房间 0。访问 0 号房间的总次数为 2,次数为偶数。
下一天你需要访问房间的编号是 (0 + 1) mod 2 = 1 - 第 2 天,你访问房间 1。这是你第一次完成访问所有房间的那天。
输入:nextVisit = [0,0,2]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,0,0,1,2,...]。
第 6 天是你访问完所有房间的第一天。
输入:nextVisit = [0,1,2,0]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,1,2,2,3,...]。
第 6 天是你访问完所有房间的第一天。
限制
n == nextVisit.length
2 <= n <= 10^5
0 <= nextVisit[i] <= i
算法
(动态规划)
-
本题的重要条件
0 <= nextVisit[i] <= i
,说明i
只能由i - 1
到达,且必须是偶数次访问i-1
,且可以推断出必须经过两次i-1才能到达i。 -
状态表示:f(i)为第一次访问第i房间时的日期。
-
状态转移:
f(i) = 第二次访问i-1房天数 + 1
-
第一次到达第
i - 1
个房间的步数f(i - 1) -
第二次到达第
i - 1
个房间的步数,我们第一次到达了第i - 1
个房间,下一步必然是走到t = nextVisit[i - 1]
号房间,推断0~i-2
房间访问的次数均为偶数(可视化0次)相当于从新开始,但是0到t可以忽略,f(i - 1) - t + 1,加的步骤。加 1是需要补上从是需要补上从i到到t的那一步。\[f(i - 1) - f(t) + 1 \] -
到达第i个房间的一步。
-
答案汇总: f[i] = 2 * f[i - 1] - f[t] + 2
-
-
初始化:f(0)=0,可以理解为第一次访问第 0个房间仅需 0 天。
时间复杂度
- 状态数为 \(O(n)\) ,转移时间为常数,故总时间复杂度为 \(O(n)\) 。
空间复杂度
- 需要 \(O(n)\) 的额外空间存储状态。
class Solution {
public:
int firstDayBeenInAllRooms(vector<int>& nextVisit) {
const int mod = 1000000007;
int n = nextVisit.size();
vector<int> f(n);
f[0] = 0;
for(int i = 1; i < n; i++){
int t = nextVisit[i - 1];
f[i] = (2 * f[i - 1] - f[t] + 2) % mod;
if(f[i] < 0) f[i] += mod;
}
return f[n - 1];
}
};