一行状态转移 C++代码
class Solution {
public:
int firstDayBeenInAllRooms(vector<int>& nextVisit) {
const int MOD = 1'000'000'007;
int n = nextVisit.size();
vector<long> f(n,0);
for (int i = 1; i < n; ++i)
f[i] = (2*f[i-1] - f[nextVisit[i-1]] + 2 + MOD) % MOD;
return f[n-1];
}
};
下一次访问 哪个房间由 访问当前房间的次数 决定
算上本次访问 访问当前房间的次数为奇数 则第二天访问的房间在nextVisit数组内
且nextVisit数组中 索引对应的房间号 总是小于等于索引 如 第4 个元素 对应的房间号 永远不可能大于4
算上本次访问 访问当前房间的次数为偶数 则第二天访问的房间为 (i + 1) % n
状态定义:f[i] 第一次访问i房间时为第f[i]天。
首先,访问第i个房间一定是 当i-1个房间的访问次数为偶数,那么下一天访问的才是第i个房间,并且第一次访问第i个房间,一定是第i-1个房间第二次被访问的下一天,则有:
第一次访问 i 房间的天数 f1[i] = 第二次访问 i - 1 房间的天数f2[i-1] + 1
对第二次访问i-1房间的天数f2[i-1]分类讨论:
如果 nextVisit[i-1] = i-1,即当访问i-1个房间的次数为奇数的时候,下一天访问的房间还是第i-1个房间,则第二次访问i-1房间的天数就是第一次访问i-1房间天数加1:
f2[i-1] = f1[i-1] + 1
如果 nextVisit[i-1] < i - 1,这说明当访问第i-1个房间之后,下一天访问的房间不再是第i-1个房间,而是另一个房间号比较小的房间,其房间号为nextVisit[i-1],那么我们要考虑的就是,从第nextVisit[i-1]个房间再次回到第i-1个房间需要多少天?首先,上一行情况下到达第nextVisit[i-1]个房间的天数为f1[i-1] + 1,即到达第i-1个房间之后,又过了一天。再次回到刚刚的问题,从第nextVisit[i-1]个房间再次回到第i-1个房间需要多少天?这是本题的重点,由于访问偶数次才能访问右边的下一个房间,所以对于i左边的房间,我们一定都访问了偶数次(不然不可能到达i)。所以,在都是偶数次的情况下,可以把他们都看作0次,这样整个过程就跟一开始的过程完全一样了,那么从第nextVisit[i-1]房间到i-1房间过去的天数就等于第一次访问第i-1个房间的天数减去第一次访问第nextVisit[i-1]的天数: f1[i-1] - f1[nextVisit[i-1]],具体过程如下
f2[i-1] = f1[i-1] + 1 + f1[i-1] - f1[nextVisit[i-1]]
= 2 * f1[i-1] - f1[nextVisit[i-1]] + 1
由于f2完全依赖f1,所以简化递推公式为:
f[n] = f[n-1] + 2 if nextVisit[n-1] == n - 1
f[n] = 2*f[n-1] - f[nextVisit[n-1]] + 2 if nextVisit[n-1] < n - 1
合并到一起就是:
f[n] = 2*f[n-1] - f[nextVisit[n-1]] + 2