每週一道算法題010:掃地機器人路徑統計

問題:

假設有一款不會反覆清掃同一個地方的機器人,它只能前後左右移動。舉個例子,如果第1次向後移動,那麼連續移動3次時,就會有以下9種情況(圖6)。又因爲第1次移動可以是前後左右4種情況,所以移動3次時全部路徑有9×4=36種。

每週一道算法題010:掃地機器人路徑統計

求這個機器人移動12次時,有多少種移動路徑?

思路:

嘗試用遞歸和非遞歸兩種辦法來解。

遞歸思路:
從起點開始,在各方向移動1步,如果移動後的點不在當前的路徑中,就加入到當前路徑中,並進行下一次移動,當移到到指定的N步時,退出,並計數加1,視爲找到一條路徑。

非遞歸思路:
1.從1開始逐一加大移動步數,直至達到N步。
2.將相同步數的路徑視爲同一批,將同一批的每一條路徑依次POP出來。
3.找到每條路徑的最後一個點,進行4個方向的移動,如果下一個點不在該路徑中,視爲一條新路徑,並存放至臨時表中。
4.待這一批所有的路徑處理完後,將臨時表賦值給全局路徑記錄表。

解答:

php:

ini_set('memory_limit','1024M');

class Machine
{
    const N = 12;
    private $directions = array(array(0, 1), array(0, -1), array(1, 0), array(-1, 0));
    private $stepList = array();

    // 移動 - 遞歸算法
    function move($log = array())
    {
        // 剛好走了N+1步,就結束本次遞歸,認爲找到了一條路徑
        if (count($log) == self::N + 1) {

            // 如果需要記錄路徑,請打開此註釋
            //$this->stepList[] = $log;
            return 1;
        }

        $cnt = 0;
        $last = end($log);
        foreach ($this->directions as $d) {
            $nextPos = array($last[0] + $d[0], $last[1] + $d[1]);
            if (!in_array($nextPos, $log)) {
                $cnt += $this->move(array_merge($log, array($nextPos)));
            }
        }
        return $cnt;
    }

    // 如果遞歸方法中開啓了路徑記錄註釋,則可以用此方法取得所有的路徑
    function getStepList()
    {
        return $this->stepList;
    }

    // 移動 - 非遞歸算法
    function move2($startPoint)
    {
        $allFootprint = array(array($startPoint));

        // 遍歷步數
        for ($i = 0; $i < self::N; $i++) {
            $allNewFootprint = array();
            while (count($allFootprint) > 0) {

                // 消費前置數據,每次從最後取一條路徑出來
                $curFootprint = array_pop($allFootprint);

                // 找到路徑中的最後一個節點
                $last = end($curFootprint);

                // 各方向走一步
                foreach ($this->directions as $d) {
                    $nextPos = array($last[0] + $d[0], $last[1] + $d[1]);

                    // 沒走過的點加入到新路徑中
                    if (!in_array($nextPos, $curFootprint)) {
                        $allNewFootprint[] = array_merge($curFootprint, array($nextPos));
                    }
                }
            }
            $allFootprint = $allNewFootprint;// 保存本次結果,作爲下一次處理的前置數據
        }
        return $allFootprint;
    }
}

$Machine = new Machine();
$rs = $Machine->move(array(array(0, 0)));
echo $rs."\n";
$rs = $Machine->move2(array(0, 0));
echo count($rs)."\n";

輸出:

324932
324932

golang:

package main

import "fmt"

type Point struct {
    X int
    Y int
}

const N = 12

var directions = [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}

func main() {
    p := Point{0, 0}
    rs := move([]Point{p})
    fmt.Println(rs)

    rs2 := move2(p)
    //for _, value := range rs2 {
    //    fmt.Println(value)
    //}
    fmt.Println(len(rs2))
}

func move(log []Point) int {
    logLength := len(log)
    if logLength == N+1 {
        return 1
    }

    cnt := 0
    last := log[logLength-1]
    for _, d := range directions {
        nextPos := Point{last.X + d[0], last.Y + d[1]}
        if !inArray(nextPos, log) {
            cnt += move(append(log, nextPos))
        }
    }
    return cnt
}

func move2(startPoint Point) [][]Point {
    allFootPrint := [][]Point{{startPoint}}
    for i := 0; i < N; i++ {
        allNewFootPrint := make([][]Point, 0)
        for len(allFootPrint) > 0 {
            // pop一條路徑
            curFootPrint := allFootPrint[len(allFootPrint)-1]
            allFootPrint = allFootPrint[:len(allFootPrint)-1]
            last := curFootPrint[len(curFootPrint)-1]
            for _, d := range directions {
                nextPoint := Point{last.X + d[0], last.Y + d[1]}
                if !inArray(nextPoint, curFootPrint) {
                    // 必須複製一份數據出來,否則會發生路徑重複
                    newCurFootPrint := make([]Point, len(curFootPrint))
                    copy(newCurFootPrint, curFootPrint)

                    allNewFootPrint = append(allNewFootPrint, append(newCurFootPrint, nextPoint))
                }
            }
        }
        allFootPrint = allNewFootPrint
    }
    return allFootPrint
}

// 檢查某個點是否在路徑中
func inArray(need Point, needArr []Point) bool {
    for _, v := range needArr {
        if need == v {
            return true
        }
    }
    return false
}

輸出:

324932
324932
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章