解析一道区块链行业前三公司的笔试题

前言

年初找工作时,面过一家自称区块链行业前三的公司(事实上也差不多),他们笔试题蛮有意思的,时隔半年,估计他们也更新题库了,就拿出来和大家分享一下。

题目

解析一道区块链行业前三公司的笔试题_第1张图片

翻译如下:

Sandy是一个制作地图的探险家。她从点(0,0)开始探索,每次可以向左、向右、向上或者向下移动一点。例如,如果Sandy站在(x,y)处,她可以移动到(x+1,y),(x-1,y),(x,y+1)或(x,y-1)。她不能跳也不能走对角线方向。

John要阻止Sandy,他会在坐标数字的绝对值之和大于21的地方埋地雷。例如,点(59,-79)有地雷,因为5+7+9+9>21;而点(-113,-104)没有地雷,因为1+1+3+1+0+4<=21。

如果Sandy踩到地雷,她会死。她不能跳过地雷,必须绕着走。

请编写一个程序,来计算Sandy从(0,0)开始可以访问的点的数量。

分析

首先,可以快速判断出这道题考的是图的遍历,遍历的方法无非深度优先,或者广度优先。此题是要计算所有可遍历的点,于是,我选择用广度优先。

然后,因为Sandy可以向四个方向移动,初步可以判断可以遍历的范围是关于XY轴对称。再则,根据地雷的埋放规则“坐标数字的绝对值之和大于21的地方埋地雷”, 可以进一步推出可以遍历的范围是关于y=x、y=-x、y=0和x=0对称的。举个例子,(56,65),(-56,65),(56,-65),(-56,-65)和(65,56),(-65,56),(65,-56),(-65,-56)都是埋放的地雷的点。

于是,我们只需要考虑x>=0,y>=0且x<=y的区域,乘以对称的次数,就可以推出所有可以遍历的点。但需要注意的是对于x>0,y>0且x0和y=x且x>0这两条边,只需要乘以4就好,因为它们是被两个区块所共有的,乘以8就算重了;特别需要注意的是(x=0,y=0)这点,是所有可以遍历的区域的中心,乘以1即可。

编码

按照上述的分析,编码工作就可以依次开始了。

首先,先定义一个关于的点的结构体,并增加一个计算坐标和的函数。

type Point struct {
    X int
    Y int
}

func (p Point) CoordinateSum() int {
    xNumStrs := strings.Split(strconv.Itoa(p.X), "")
    yNumStrs := strings.Split(strconv.Itoa(p.Y), "")
    numStrs := append(xNumStrs, yNumStrs...)

    sum := 0
    for _, str := range numStrs {
        num, _ := strconv.ParseInt(str, 10, 32)
        sum += int(num)
    }

    return sum
}

CoordinateSum不会改变点的X,Y值,于是,定义和值类型的方法。

接着,在通用的遍历图算法,需要有个map判断当前点是否遍历过,来剪枝。在本题中,还需要判断当前点是否是地雷,于是,我把这两段逻辑写到了一方法里。

var checkedPoints map[Point]bool

func CheckPoint(p Point) bool {
    if _, ok := checkedPoints[p]; ok {
        return false
    }

    if p.Y > p.X || p.X < 0 || p.Y < 0{
        return false
    }

    return p.CoordinateSum() <= 21
}

全局变量在工程化的代码中是需要尽量避免使用的,这里图方便,权且用一下。

下面就是广度优先遍历的代码了。

var toCheckPoints = list.New()
checkedPoints = map[Point]bool{}
var point = Point{0, 0}

toCheckPoints.PushFront(point)
checkedPoints[point] = true

for toCheckPoints.Len() > 0 {
    element := toCheckPoints.Front()
    point = toCheckPoints.Remove(element).(Point)

    searchPoint := Point{point.X, point.Y - 1}
    Search(searchPoint, toCheckPoints)

    searchPoint = Point{point.X - 1, point.Y}
    Search(searchPoint, toCheckPoints)

    searchPoint = Point{point.X + 1, point.Y}
    Search(searchPoint, toCheckPoints)

    searchPoint = Point{point.X, point.Y + 1}
    Search(searchPoint, toCheckPoints)
}
    
func Search(searchPoint Point, toCheckPoints *list.List) {
    if CheckPoint(searchPoint) {
        checkedPoints[searchPoint] = true
        toCheckPoints.PushFront(searchPoint)
    } else {
        if _, ok := checkedPoints[searchPoint]; !ok {
            checkedPoints[searchPoint] = false
        }
    }
}

最后,就是根据遍历的过的点,计算可以遍历点的数量。

pointsOnMap := 0
var minedPoints = list.New()
var visiblePoints = list.New()

for point, visible := range checkedPoints {
    if visible {
        if point.X == 0 && point.Y == 0 {
            pointsOnMap += 1
        } else if point.Y == 0 || point.X == point.Y {
            pointsOnMap += 4
        } else {
            pointsOnMap += 8
        }
        visiblePoints.PushFront(point)
    }else {
        minedPoints.PushFront(point)
    }
}
fmt.Println("Points on map are", pointsOnMap)

程序写好,但是不确定对不对怎么办?

这个吗,就需要把可以遍历的点和地雷分别画出来,就可以知道。

这里,我选择了用Echarts的散点图的在线示例来画。地址是https://echarts.baidu.com/examples/editor.html?c=scatter-simple

使用的配置是:

option = {
    dataZoom: [
        {
            type: 'slider',
            show: true,
            xAxisIndex: [0],
            start: 0,
            end: 500,
        },
        {
            type: 'slider',
            show: true,
            yAxisIndex: [0],
            start: 0,
            end: 250
        }
    ],
    xAxis: {},
    yAxis: {},
    series: [{
        symbolSize: 1,
        data: [
            [10.0, 8.04],
        ],
        type: 'scatter'
    }]
};

用算出来的点,替换data部分就可以画出来了。

可以遍历的点的图是:


解析一道区块链行业前三公司的笔试题_第2张图片

地雷的点的图是:


解析一道区块链行业前三公司的笔试题_第3张图片

完整代码

package main

import (
    "container/list"
    "fmt"
    "strconv"
    "strings"
)

type Point struct {
    X int
    Y int
}

func (p Point) CoordinateSum() int {
    xNumStrs := strings.Split(strconv.Itoa(p.X), "")
    yNumStrs := strings.Split(strconv.Itoa(p.Y), "")
    numStrs := append(xNumStrs, yNumStrs...)

    sum := 0
    for _, str := range numStrs {
        num, _ := strconv.ParseInt(str, 10, 32)
        sum += int(num)
    }

    return sum
}

var checkedPoints map[Point]bool

func main() {

    var toCheckPoints = list.New()
    checkedPoints = map[Point]bool{}
    var point = Point{0, 0}

    toCheckPoints.PushFront(point)
    checkedPoints[point] = true

    for toCheckPoints.Len() > 0 {
        element := toCheckPoints.Front()
        point = toCheckPoints.Remove(element).(Point)

        searchPoint := Point{point.X, point.Y - 1}
        Search(searchPoint, toCheckPoints)

        searchPoint = Point{point.X - 1, point.Y}
        Search(searchPoint, toCheckPoints)

        searchPoint = Point{point.X + 1, point.Y}
        Search(searchPoint, toCheckPoints)

        searchPoint = Point{point.X, point.Y + 1}
        Search(searchPoint, toCheckPoints)
    }

    //fmt.Println(len(checkedPoints))
    //fmt.Println(checkedPoints)

    pointsOnMap := 0
    var minedPoints = list.New()
    var visiblePoints = list.New()

    for point, visible := range checkedPoints {
        if visible {
            if point.X == 0 && point.Y == 0 {
                pointsOnMap += 1
            } else if point.Y == 0 || point.X == point.Y {
                pointsOnMap += 4
            } else {
                pointsOnMap += 8
            }
            visiblePoints.PushFront(point)
        }else {
            minedPoints.PushFront(point)
        }
    }
    fmt.Println("Points on map are", pointsOnMap)

    //for e := minedPoints.Front();  e != nil; e = e.Next()  {
    //  p := e.Value.(Point)
    //  fmt.Println("[", p.X, ",", p.Y,"],")
    //}

}

func Search(searchPoint Point, toCheckPoints *list.List) {
    if CheckPoint(searchPoint) {
        checkedPoints[searchPoint] = true
        toCheckPoints.PushFront(searchPoint)
    } else {
        if _, ok := checkedPoints[searchPoint]; !ok {
            checkedPoints[searchPoint] = false
        }
    }
}

func CheckPoint(p Point) bool {
    if _, ok := checkedPoints[p]; ok {
        return false
    }

    if p.Y > p.X || p.X < 0 || p.Y < 0{
        return false
    }

    return p.CoordinateSum() <= 21
}

答案:287881

后记

笔试我是答对了,但是也没能得到面试机会……

HR反馈说,面试官觉得我的年龄和实力不符合团队结构的期望 ˚‧º·(˚ ˃̣̣̥⌓˂̣̣̥ )‧º·˚

那次面试,也最终断了我继续在区块链行业奋斗的希望。没办法,我可是要工作养家的男人哈。

你可能感兴趣的:(解析一道区块链行业前三公司的笔试题)