今天分享周赛遇到的一道题(257周赛第3题),这道题在比赛时第一次见到时直接就懵了,不知道从何做起,知道是动态规划却推不出转移方程,比赛结束补题发现是一道很有意思的题目,值得记录一下。
你需要访问 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
取余后的结果。
示例 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是因为有可能出现负数的情况,要转成正数。
题目链接:
1997. 访问完所有房间的第一天