GuilinDev

Lc0403

05 August 2008

403 Frog Jump

一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。

给你石子的位置列表 stones(用单元格序号 升序 表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。

开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格 1 跳至单元格 2 )。

如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1 个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。

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
48
49
50
51
class Solution {
    /*
    https://segmentfault.com/a/1190000007648898
    有点类似于jump game, 只不过这里对步数有了隐形的要求,当前步数和之前步数有关。
如果我们用brute force的方法来解,每一步有三种可能,一共n个石头,时间复杂度就是O(3^n)。
这其中有很多步数是多余的,第i个石头无法从任何一个石头到达,那么我们应该立即中止搜寻。
还有一个石头可能由之前的多个石头到达,这又是可以优化的地方。
当前结果可由之前的结果得出,且有重复的搜索方法,就需要用DP。
暴力搜索其实可以通过的
    */
    public boolean canCross(int[] stones) {
        if (stones[1] != 1) {
            return false;
        }
        int n = stones.length;
        int[][] dp = new int[n][n];// for ith stones, j means maximun previous steps
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                dp[i][j] = -1;
            }
        }
        return canCross(dp, stones, 0, 0, n);
    }
    
    private boolean canCross(int[][] dp, int[] stones, int i, int j, int n) {
        if (dp[i][j] != -1) {
            return dp[i][j] == 1;
        }
        if (i == n-1) {
            dp[i][j] = 1;
            return true;
        }
        
        for (int k = i + 1; k < n; k++) {
            if (stones[k] < stones[i] + j - 1) {//too close
                continue;
            } else if (stones[k] > stones[i] + j + 1) {//too far
                dp[i][j] = 0;
                continue;
            } else {// j-1, j, j+1 three possibility 
                if (canCross(dp, stones, k, stones[k] - stones[i], n)) {
                    dp[i][j] = 1;
                    return true;
                }
            }
        }
        dp[i][j] = 0;
        return false;
    }
}