每周一道算法题010:扫地机器人路径统计

问题:

假设有一款不会反复清扫同一个地方的机器人,它只能前后左右移动。举个例子,如果第1次向后移动,那么连续移动3次时,就会有以下9种情况(图6)。又因为第1次移动可以是前后左右4种情况,所以移动3次时全部路径有9×4=36种。

求这个机器人移动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

原文地址:https://blog.51cto.com/ustb80/2431586

时间: 2024-08-29 17:35:58

每周一道算法题010:扫地机器人路径统计的相关文章

每周一道算法题005:切木棒

问题: 假设要把长度为n厘米的木棒切分为1厘米长的小段,但是1根木棒只能由1人切分,当木棒被切分为3段后,可以同时由3个人分别切分木棒.求最多有m个人时,最少要切分几次. 譬如n=8,m=3时如下图所示,切分4次就可以了. 求当n=20,m=3时的最少切分次数.求当n=100,m=5时的最少切分次数. 思路: 这道题最难的不是算法,而是理解题意.木棒刚开始只有1根,题目规定"1根木棒只能由1人切分",此时由1人切分后,变成2根:2根木棒再做切分,也要满足"1根木棒只能由1人切

每周一道算法题003:翻牌

问题: 有100张写着数字1-100的牌,并按顺序排列.最开始所有牌都是背面朝上放置.某人从第2张牌开始,隔1张牌翻牌.然后第2, 4, 6, -,100张牌就会变成正面朝上.接下来,另一个人从第3张牌开始,隔2张牌翻牌(原本背面朝上的,翻转成正面朝上:原本正面朝上的,翻转成背面朝上).再接下来,又有一个人从第4张牌开始,隔3张牌翻牌.像这样,从第n张牌开始,每隔n-1张牌翻牌,直到没有可翻动的牌为止.求当所有牌不再变动时,所有背面朝上的牌的数字. 思路: 这道题思路有很多种: 思路1设i为轮次

每周一道算法题001:回文数

题目: 找出大于10的最小的2进制,8进制,10进制都是回文数的最小的数.回文数指的是正读和反读都是一样的数,例如:33,10001,123454321... 思路: 先转换进制,然后统一处理成字符串进行比较 解答: PHP function execute(){ $x = 11; while (1) { if ($x == strrev($x) && decbin($x) == strrev(decbin($x)) && decoct($x) == strrev(deco

每周一道算法题002:四则运算

问题: 求位于1000-9999,满足如下条件的数: 各位数字间加入四则运算符,也可省略,使得按四则运算计算的结果为原数字的各位数逆序排列. 例如:351 → 3×51 = 153 思路: 遍历1000-9999所有的数字,切分并组合运算符,拼成四则运算的算式然后计算.4位数中最大的数是9999,他可以被拆成999+9=1008,逆排后不可能等于原数,减法和除法不可能计算出比原数更大的数,所以只需要考虑乘法. php提供了eval函数,可以很方便的计算,但是需要对数字进行处理,因为08开头的数字

每周一道算法题006:抽签组合

问题: 有如下3支队伍,每个队伍都有2名队员.team1:A,B;team2:C,D;team3:E,F; 现在每个队出1个人,组成一个队去探险,请列出所有的组队方式. 思路: 这就是一个组合的问题,每个队里挑一人,那么总共应该有222=8种组合方式.如果暴力求解,那就是三层循环嵌套.但如果问题扩展一下,变成10个队,每个队10人,就无法暴力求解了,至少代码是没有扩展性的. 有如下一种思路: 循环所有的队伍第一次取出A,B两名队员,存起来:第二次取出C,D两名队员,与前一轮存下的队员进行交叉组合

每周一道算法题:兑换零钱

问题: 已知可兑换的零钱种类有1元,5元,10元,20元4种,现在有100块钱要换成零钱且总数量少于15张,有几种换法?分别是什么? 思路: 已知有[1,5,10,20]这样的一个可选数据集S,现在要从中取出n个数,每个数的张数为a,使得a1xn1+a2xn2+...aixni = 100. 最大的面额是20,总共需要100/20=5张,这是最少的张数,所以循环的下限是5,上限题目已经限定了,是15. 也就是说,从S这个数据集中,取5-15个数,使得他们的和为100,数是可以重复的. 先考虑最简

每周一道算法题009:找二进制对称的日期

题目: 把年月日表示为YYYYMMDD这样的8位整数,然后把这个整数转换成二进制数并且逆序排列,再把得到的二进制数转换成十进制数,求与原日期一致的日期.求得的日期要在上一次东京奥运会(1964年10月10日)到下一次东京奥运会(预定举办日期为2020年7月24日)之间. 思路: 从起始时间开始逐天累加,对每一天进行进制转换并反转,然后比较,如果相同就输出,不同就继续,直至到达结束时间. 解答: php: function findDate($begin, $end) { $beginDate =

每周一道算法题011:最长公共子串

问题: 求以下几组单词的最长公共子串的长度1.fish和fosh2.fish和hish3.fish和vista 思路: 可以用表格法,横纵坐标分别是两个单词,如果字符相同,就用左上角的数字加1,最后取表格中的最大值. 解答: php: <?php // 找出两个单词的最长公共子串 function findLongestSubString($word1, $word2) { $len1 = strlen($word1); $len2 = strlen($word2); $cell = array

前端面试的一道算法题

(使用canvas解答) 下面说一个跟前端有点相关并且有点趣的一道算法题. 题目: 平面上有若干个不特定的形状,如下图所示.请写程序求出物体的个数,以及每个不同物体的面积. 分析 想要知道有多少个图形,想到的就是先获取图片中的每一个像素点然后判获取像素点的背景颜色(RGBA).想要获得图片中的每一个像素点,那就可以联想到使用h5的canvas.如下: 菜鸟教程中canvas的getimagedata方法http://www.runoob.com/tags/canvas-getimagedata.