[leetcode] 1997-访问完所有房间的第一天

今天分享周赛遇到的一道题(257周赛第3题),这道题在比赛时第一次见到时直接就懵了,不知道从何做起,知道是动态规划却推不出转移方程,比赛结束补题发现是一道很有意思的题目,值得记录一下。

访问完所有房间的第一天

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

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

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

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

示例 1:

输入:nextVisit = [0,0]
输出:2
解释:
- 第 0 天,你访问房间 0 。访问 0 号房间的总次数为 1 ,次数为奇数。
  下一天你需要访问房间的编号是 nextVisit[0] = 0
- 第 1 天,你访问房间 0 。访问 0 号房间的总次数为 2 ,次数为偶数。
  下一天你需要访问房间的编号是 (0 + 1) mod 2 = 1
- 第 2 天,你访问房间 1 。这是你第一次完成访问所有房间的那天。

示例 2:

输入:nextVisit = [0,0,2]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,0,0,1,2,...] 。
第 6 天是你访问完所有房间的第一天。

示例 3:

输入:nextVisit = [0,1,2,0]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,1,2,2,3,...] 。
第 6 天是你访问完所有房间的第一天。

提示:

  • n == nextVisit.length
  • 2 <= n <= 105
  • 0 <= nextVisit[i] <= i

备忘录(超时):

const int mod = 1e9+7;
const int maxn = 1e5+5;
class Solution {
public:
    long long f[maxn], s[maxn];
    int firstDayBeenInAllRooms(vector& nextVisit) {
        int f[maxn], s[maxn];
        int cur = 0, len = 0, n = nextVisit.size(), ans = 0;
        while(cur != n - 1){
            if(s[cur] == 0){
                len = 1;
                s[cur] = 1;
                ans = (ans + 1) % mod;
                cur = nextVisit[cur];
            }else if(s[cur] == 1){
                f[cur] = len;
                s[cur] = 2;
                ans = (ans + 1) % mod;
                cur ++;
            }else{
                len = (len + f[cur] + 1) % mod;
                ans = (ans + f[cur] + 1) % mod;
                cur ++;
            }
        }
		return ans;
    }
};

解题思路:

这个题的题目里有一个重要的条件0 <= nextVisit[i] <= i,这条件意味着每一次新到达第一个房间,就要往回走,或者不走,想要前进至少要访问前一个房间两次,那么就有这样一个过程,第奇数次到达房间然后到下一次到达这个房间所需要的次数,这是可以记录的,我们可以用f数组来存储两次访问房间的间隔,然后在下次访问时直接加上就可以了。再开一个数组s表示该房间的状态,0是第一次访问,开始用len记录步数,1是第二次访问,len记录结束,存入f,2是f已经记录完,可以直接查看第i号房间来回一次的次数。总的看来还是模拟,已经可以接受大部分数据了,但当数据量大到一定程度时就会超时。

前缀和优化

const int mod = 1e9+7;
const int maxn = 1e5+5;
class Solution {
public:
    long long f[maxn];
    int firstDayBeenInAllRooms(vector& nextVisit) {
        int n = nextVisit.size();
        for(int i = 1; i < n; ++ i){
            f[i] = (2 * f[i - 1] - f[nextVisit[i - 1]] + 2 + mod) % mod; 
        }
        return f[n - 1];
    }
};

解题思路:

这一次数组f不记录经过一个房间两次的次数了,我改成记录到达一个房间所需要的次数,我将一个人从第一次访问i-1号房间到第一次访问i号房间分成几个阶段。

第一阶段:因为0 <= nextVisit[i] <= i,所以第一步要往回走,步数1,到达nextVisit[i-1]

第二阶段: 现在相当于第一次到nextVisit[i-1],现在的目的是到i-1,f[i]是记录第一次到i的时间所以,nextVisit[i-1]到i-1的时间为f[i - 1] - f[nextVisit[i - 1]],到达i-1

第三阶段:现在是第二次到达i-1了,所以直接向前一步到达i,步数为1

所以f[i] = (1 + f[i - 1] - f[nextVisit[i - 1]] + 1 + f[i - 1] + mod) % mod

加mod是因为有可能出现负数的情况,要转成正数。

[leetcode] 1997-访问完所有房间的第一天_第1张图片

题目链接:

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

你可能感兴趣的:(刷题,leetcode,动态规划)