// 声明包为 main,表示这是一个可执行程序 package main import ( "fmt" // 导入 fmt 包,用于格式化输入输出 "time" // 导入 time 包,用于计算程序运行时间 ) // 定义常量 N,代表棋盘的大小为 8x8 const N = 8 var ( // dx 和 dy 数组定义了马在棋盘上可以移动的8个方向的 x 和 y 坐标变化 // 例如,(dx[0], dy[0]) = (2, 1) 表示马可以从 (x, y) 移动到 (x+2, y+1) dx = []int{2, 1, -1, -2, -2, -1, 1, 2} dy = []int{1, 2, 2, 1, -1, -2, -2, -1} // stepCount 用于记录算法关键步骤的执行次数,主要用于性能分析 stepCount int64 ) // isValid 函数检查给定坐标 (x, y) 是否在棋盘内,并且该位置尚未被访问过 // board[x][y] == -1 表示该位置未被访问 func isValid(x, y int, board [][]int) bool { stepCount++ // 每次检查都增加关键步骤计数 return x >= 0 && x < N && y >= 0 && y < N && board[x][y] == -1 } // getDegree 函数计算从 (x, y) 位置出发,有多少个可行的下一步(即“出度” // 这是 Warnsdorff 规则的核心,用于优化路径选择 func getDegree(x, y int, board [][]int) int { count := 0 // 遍历8个可能的移动方向 for i := 0; i < 8; i++ { // 如果移动后的位置是有效的,则计数器加一 if isValid(x+dx[i], y+dy[i], board) { count++ } } return count } // solveKnightTour 是解决马踏棋盘问题的主要递归函数 // x, y: 当前马的位置 // moveCount: 当前是第几步 // board: 棋盘状态 func solveKnightTour(x, y, moveCount int, board [][]int) bool { // 将当前位置标记为第 moveCount 步 board[x][y] = moveCount // 如果已经走满了 N*N-1 步(步数从0开始),说明已经找到了一个完整的路径 if moveCount == N*N-1 { return true // 返回 true 表示成功 } // 定义一个结构体 Move,用于存储下一步的位置和该位置的“出度” type Move struct { x, y, degree int } // 创建一个切片,用于存储所有可能的下一步 moves := make([]Move, 0, 8) // 遍历8个方向,找出所有有效的下一步 for i := 0; i < 8; i++ { nx, ny := x+dx[i], y+dy[i] if isValid(nx, ny, board) { // 如果是有效的一步,计算该点的“出度”并存入 moves 切片 degree := getDegree(nx, ny, board) moves = append(moves, Move{nx, ny, degree}) } } // 使用选择排序对所有可能的下一步进行排序,按照“出度”从小到大排序 // 这是 Warnsdorff 规则的应用:优先选择“出度”最少的点,这样可以减少后面出现“死路”的可能性 for i := 0; i < len(moves); i++ { for j := i + 1; j < len(moves); j++ { if moves[j].degree < moves[i].degree { moves[i], moves[j] = moves[j], moves[i] } } } // 按照排序后的顺序,依次尝试每一个可能的下一步 for _, move := range moves { // 递归调用 solveKnightTour,进入下一步 if solveKnightTour(move.x, move.y, moveCount+1, board) { return true // 如果递归返回 true,说明找到了解,直接返回 true } } // 如果所有可能的下一步都无法导致一个成功的解,则进行“回溯” // 将当前位置重置为未访问状态 (-1),并返回 false board[x][y] = -1 return false } // printBoard 函数用于打印最终的棋盘路径 func printBoard(board [][]int) { fmt.Println("\n马踏棋盘路径:") for i := range N { for j := range N { // 使用 %3d 格式化输出,使棋盘对齐 fmt.Printf("%3d", board[i][j]) } fmt.Println() // 每行结束后换行 } } // main 函数是程序的入口 func main() { var startX, startY int // 提示用户输入起始位置 fmt.Print("请输入起始位置 (x y, 范围0-7): ") // 读取用户输入的坐标 fmt.Scanf("%d %d", &startX, &startY) // 检查输入的坐标是否在有效范围内 if startX < 0 || startX >= N || startY < 0 || startY >= N { fmt.Println("输入位置无效!") return // 如果无效,程序退出 } // 初始化棋盘,创建一个 N*N 的二维切片 board := make([][]int, N) for i := range board { board[i] = make([]int, N) // 将棋盘所有位置初始化为 -1,表示都未被访问 for j := range board[i] { board[i][j] = -1 } } // 重置关键步骤计数器 stepCount = 0 // 记录算法开始时间 start := time.Now() // 调用 solveKnightTour 函数开始求解 if solveKnightTour(startX, startY, 0, board) { // 如果求解成功 elapsed := time.Since(start) // 计算总耗时 printBoard(board) // 打印棋盘 fmt.Printf("\n求解成功!\n") fmt.Printf("运行时间: %v\n", elapsed) fmt.Printf("关键步骤执行次数: %d\n", stepCount) } else { // 如果求解失败 fmt.Println("无解!") } }