0%

1871. 跳跃游戏 VII

1871.跳跃游戏 VII

题目描述:

给你一个下标从 0 开始的二进制字符串 s 和两个整数 minJumpmaxJump 。一开始,你在下标 0 处,且该位置的值一定为 '0' 。当同时满足如下条件时,你可以从下标 i 移动到下标 j 处:

  • i + minJump <= j <= min(i + maxJump, s.length - 1)
  • s[j] == '0'.

如果你可以到达 s 的下标 s.length - 1 处,请你返回 true ,否则返回 false

数据范围:

$2\le s.len \le 10^5$

题解:

不像之前的每次可以至多跳 $num$ 个,而且不是每个位置都可以起跳。可以想象到,最终得到的是一些区间,可以针对遍历到的下标 $i$ 使用区间加 $[i + minJump, i + maxJump]$ ,得到下一段覆盖的区间。对于一个位置 $i$ 判断是否能起跳,需要看是否被区间覆盖了。

线段树:

很简单,直接维护区间加,判断某一个位置是否被区间覆盖了即可。

动态规划:

$dp[i] = dp[i - maxJump] | \cdots|dp[i - minJump]$ ,但是复杂度比较高。可以把 $|$ 运算转化为求区间和运算。每次针对 $i$ 求区间 $[i - maxJump, i - minJump]$ 区间和,如果区间和大于 $0$ 则说明可以跳到。然后需要边遍历边维护区间和, $preSum[i] = preSum[i - 1] + (sum \neq 0)$ 。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const static long long mod = 1e9 + 7;
const long long INF_LL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
bool canReach(string s, int minJump, int maxJump)
{
if (s.back() != '0')
return false;
int n = s.length();
vector<int> dp(n);
vector<int> preSum(n + 1);
function<int(int)> getPreSum = [&](int index)
{
if (index < 0)
return 0;
return preSum[index];
};
preSum[1] = 1;
for (int i = 2; i <= n; ++i)
{
if (s[i - 1] == '0')
{
// [i - maxJump, i - minJump]
int sum = getPreSum(i - minJump) - getPreSum(i - maxJump - 1);
preSum[i] = preSum[i - 1] + (sum != 0);
if (i == n && sum != 0)
return true;
}
else
{
preSum[i] = preSum[i - 1];
}
}
return false;
}
};