解析一道區塊鏈行業前三公司的筆試題

前言

年初找工做時,面過一家自稱區塊鏈行業前三的公司(事實上也差很少),他們筆試題蠻有意思的,時隔半年,估計他們也更新題庫了,就拿出來和你們分享一下。html

題目

翻譯以下:面試

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。bash

若是Sandy踩到地雷,她會死。她不能跳過地雷,必須繞着走。app

請編寫一個程序,來計算Sandy從(0,0)開始能夠訪問的點的數量。echarts

分析

首先,能夠快速判斷出這道題考的是圖的遍歷,遍歷的方法無非深度優先,或者廣度優先。此題是要計算全部可遍歷的點,因而,我選擇用廣度優先。ide

而後,由於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且x<y的區域,能夠訪問點能夠乘以8;對於x>0和y=x且x>0這兩條邊,只須要乘以4就好,由於它們是被兩個區塊所共有的,乘以8就算重了;特別須要注意的是(x=0,y=0)這點,是全部能夠遍歷的區域的中心,乘以1便可。區塊鏈

編碼

按照上述的分析,編碼工做就能夠依次開始了。ui

首先,先定義一個關於的點的結構體,並增長一個計算座標和的函數。

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的散點圖的在線示例來畫。地址是echarts.baidu.com/examples/ed…

使用的配置是:

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部分就能夠畫出來了。

能夠遍歷的點的圖是:

地雷的點的圖是:

完整代碼

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反饋說,面試官以爲個人年齡和實力不符合團隊結構的指望 ˚‧º·(˚ ˃̣̣̥⌓˂̣̣̥ )‧º·˚

那次面試,也最終斷了我繼續在區塊鏈行業奮鬥的但願。沒辦法,我但是要工做養家的男人哈。

相關文章
相關標籤/搜索