跳跃游戏
链接:https://leetcode.cn/problems/jump-game/
最简单的想法就是记忆化搜索(dfs + 记忆数组)
code
bool dfs(int* nums, int numsSize, int index, int* visited) {
if(visited[index]) return false;
visited[index] = 1;
if(index >= numsSize - 1 || nums[index] + index >= numsSize - 1) return true;
if(nums[index] == 0) return false;
for(int i = nums[index]; i >= 1; i--) {
if(dfs(nums, numsSize, index + i, visited)) return true;
}
return false;
}
bool canJump(int* nums, int numsSize) {
int* visited = calloc(numsSize, sizeof(int));
return dfs(nums, numsSize, 0, visited);
}
不过效率很低…
看了下优解:可以采用正向遍历,嗯,有点类似倍增算法 + ST表的味道。
code
bool canJump(int* nums, int numsSize) {
int max_reach = 0;
for(int i=0;i<numsSize;i++)
{
if(i>max_reach)
{
return false;
}
else if(i+nums[i] >= numsSize-1)
{
return true;
}
else if(i+nums[i] >max_reach)
{
max_reach = i+nums[i];
}
}
return false;
}
不过反向遍历我认为是此题的最优解,而且代码实现也很优雅、简洁!
code
bool canJump(int* nums, int numsSize) {
int pos = numsSize - 1;
for(int i = pos - 1; i >= 0; i--) {
if(i + nums[i] >= pos) pos = i;
}
return pos == 0;
}
跳跃游戏 II
链接:https://leetcode.cn/problems/jump-game-ii/
就是基础的BFS暴力就可以过了(不过要注意特判 numsSize == 1 的情况)
code
int jump(int* nums, int numsSize) {
if(numsSize == 1) return 0;
int* queue = calloc(numsSize, sizeof(int));
int left = 0, right = 0;
queue[right++] = 0;
int* visited = calloc(numsSize, sizeof(int));
visited[0] = 1;
int cnt = 0;
while(left < right) {
int n = right - left;
cnt++;
while(n--) {
int x = queue[left++];
for(int i = 1; i <= nums[x]; i++) {
int y = i + x;
if(y >= numsSize - 1) {
goto here;
}
if(!visited[y]) {
visited[y] = 1;
queue[right++] = y;
}
}
}
}
here:;
free(queue);
free(visited);
return cnt;
}
再贴下其他优解:
code
/*
动态规划:性能差,比较暴力,但十分直观,易上手
int jump(int* nums, int numsSize) {
int dp[numsSize] ;
dp[0] = 0 ;
for(int i = 1 ; i < numsSize ; i++)
{
dp[i] = numsSize + 1 ;
}
for(int i = 1 ; i < numsSize ; i++)
{
for(int j = 0 ; j < i ; j++)
{
if(nums[j] + j >= i)
{
dp[i] = fmin(dp[i] , dp[j] + 1) ;
}
}
}
return dp[numsSize - 1] ;
}
*/
/*
反向查找出发位置(贪心)
int jump(int* nums, int numsSize) {
int position = numsSize - 1;
int steps = 0;
while (position > 0) {
for (int i = 0; i < position; i++) {
if (i + nums[i] >= position) {
position = i;
steps++;
break;
}
}
}
return steps;
}
*/
/* 正向查找可到达的最大位置(贪心)
int jump(int* nums, int numsSize) {
// maxPos用于记录从当前位置出发,能够跳到的最远位置
int maxPos = 0;
// end表示在当前跳跃步数下,能够到达的最远距离的边界
int end = 0;
// steps记录跳跃的总步数,初始化为0
int steps = 0;
for (int i = 0; i < numsSize - 1; ++i) {
// 判断当前位置i是否在当前已知的最远可到达位置maxPos范围内
// 如果在范围内,说明可以从位置i出发继续探索更远的位置
if (maxPos >= i) {
// 更新maxPos,取当前的maxPos和从位置i跳跃nums[i]步能到达的位置i + nums[i]中的较大值
maxPos = fmax(maxPos , i + nums[i]) ;
// 当遍历到的位置i等于当前跳跃步数下能到达的最远距离边界end时
// 说明当前跳跃步数已经到达极限,需要进行下一次跳跃
if (i == end) {
// 将end更新为新的最远可到达位置maxPos
end = maxPos;
// 跳跃步数加1
++steps;
}
}
}
// 返回跳跃的总步数,即到达数组最后一个位置的最小跳跃次数
return steps;
}
*/
// 建桥,与正向查找可到达的最大位置(贪心)相同意思
int jump(int* nums, int numsSize) {
int ans = 0 ;
int cur_right = 0 ;
int next_right = 0 ;
for(int i = 0 ; i < numsSize - 1 ; i++)
{
next_right = fmax(next_right , nums[i] + i) ;
if(i == cur_right)
{
ans++ ;
cur_right = next_right ;
}
}
return ans ;
}
跳跃游戏 III
链接:https://leetcode.cn/problems/jump-game-iii/
还是经典 BFS(感觉和上一题类似)
code
bool canReach(int* arr, int arrSize, int start) {
int* visited = calloc(arrSize, sizeof(int));
visited[start] = 1;
int* pi_0 = calloc(arrSize, sizeof(int));
int cnt = 0;
for(int i = 0; i < arrSize; i++) {
if(arr[i] == 0) {
pi_0[cnt++] = i;
}
}
int queue[arrSize];
int left = 0, right = 0;
queue[right++] = start;
while(left < right) {
int x = queue[left++];
int i = x + arr[x];
int j = x - arr[x];
if(i < arrSize) {
for(int k = 0; k < cnt; k++) {
if(i == pi_0[k]) {
free(visited);
free(pi_0);
return true;
}
}
if(!visited[i]) {
visited[i] = 1;
queue[right++] = i;
}
}
if(j >= 0) {
for(int k = 0; k < cnt; k++) {
if(j == pi_0[k]) {
free(visited);
free(pi_0);
return true;
}
}
if(!visited[j]) {
visited[j] = 1;
queue[right++] = j;
}
}
}
free(visited);
free(pi_0);
return false;
}
当然 DFS 同样也可以实现,搭配回溯算法就可以了。
code
int cnt;
bool dfs(int* arr, int arrSize, int start, int* pi_0, int* visited) {
if(start < 0 || start >= arrSize) return false;
for(int i = 0; i < cnt; i++) {
if(pi_0[i] == start) return true;
}
if(!visited[start]) visited[start] = 1;
else return false;
return dfs(arr, arrSize, start + arr[start], pi_0, visited) || dfs(arr, arrSize, start - arr[start], pi_0, visited);
visited[start] = 0; // 回溯
}
bool canReach(int* arr, int arrSize, int start) {
int* visited = calloc(arrSize, sizeof(int));
int* pi_0 = calloc(arrSize, sizeof(int));
cnt = 0;
for(int i = 0; i < arrSize; i++) {
if(arr[i] == 0) {
pi_0[cnt++] = i;
}
}
return dfs(arr, arrSize, start, pi_0, visited);
}
不过很显然效率不如BFS…