1. 程式人生 > >劍指Offer-68-機器人的運動範圍

劍指Offer-68-機器人的運動範圍

專案地址:https://github.com/SpecialYy/Sword-Means-Offer

問題

地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k為18時,機器人能夠進入方格(35,37),因為3+5+3+7 = 18。但是,它不能進入方格(35,38),因為3+5+3+8 = 19。請問該機器人能夠達到多少個格子?

解析

意思是從方格的左上角到右下角所有可走的節點的總數,雖然題目說了可以走四個方向,其實走右,下兩個方向即可包括全部過程。因為路徑的整體方向是左上到右下,所以我們走右,下就可以到達方格的最後一格。

對於這種不確定走多少格子,且在每個節點都有多種選擇問題都可以用回溯法來解決。

此題與這道題類似。

思路一

思路一就是通過經典的回溯法來解決,又稱深度優先遍歷。回溯法是思想其實就是暴力窮舉法,它在所有的解空間尋找可行解,只不過它不是盲目的窮舉,而是按照一定規則(也就是深度優先)來搜尋可行解。這種策略廣泛用在搜尋過程中會面臨多種選項的應用問題上。

我們從(0,0)出發,每次都從右和下兩個方向中選擇一個方向進行前進,直到走到方格邊界或位於不合法節點(題目的約束條件)就向上一個節點回溯,然後換另一個方向繼續探索。因為題目要求統計可以走的節點數,所以我們在遍歷的過程,都會統計一個節點的兩個方向中可走的節點數。這樣就需要對統計過節點進行標註已訪問過,避免重複計數。用一個與方格同樣大小的標記陣列即可解決訪問狀態問題。

	public int movingCount(int threshold, int rows, int cols) {
        if (threshold < 0 || rows <= 0 || cols <= 0) {
            return 0;
        }
        boolean[][] visited = new boolean[rows][cols];
        return dfs(threshold, rows, cols, 0, 0, visited);
    }

    public int dfs(int threshold,
int rows, int cols, int row, int col, boolean[][] visited) { if (row < 0 || row >= rows || col < 0 || col >= cols || visited[row][col] || !isValid(row, col, threshold)) { return 0; } int count = 1; //標記已訪問過的節點 visited[row][col] = true; //因為是從左上到右下,所以只需探索右,下方向即可 count += dfs(threshold, rows, cols, row + 1, col, visited); count += dfs(threshold, rows, cols, row , col + 1, visited); //note: 此處不用恢復現場,因為這裡的節點訪問不要求順序性,所以要避免重複訪問。 return count; } /** * 判斷當前節點是否滿足約束 * @param row * @param col * @param threshold * @return */ private boolean isValid(int row, int col, int threshold) { int result = countOfEachDigit(row); result += countOfEachDigit(col); return result <= threshold; } /** * 求一個數各位的之和 * @param num * @return */ private int countOfEachDigit(int num) { int result = 0; while (num != 0) { result += num % 10; num /= 10; } return result; }

總結

雖然回溯法要注意探索和恢復現場兩點,前提是結合題意來說的,如果探索的節點對於順序性無要求,那麼就不必要恢復現場了。