九宫格问题(回溯的多种写法,Go语言实现)

九宫格问题(回溯法,Go语言实现)

 

问题重现:

有1~10十个数,从中选出不重复的9个数填入到九宫格,现要求相邻(上下、左右)的两数之和为质数,问有多少种填法?

此题比较简单,所以直接给代码了。

解法一

package main

import (
	"fmt"
)

var pos [9]int
var sub []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var num []int = []int{1, 2, 3, 5, 7, 11, 13, 17, 19}

/*从质数中查找,找到返回true*/
func searchFromNum(n int) bool {
	for i := 0; i < 9; i++ {
		if n == num[i] {
			return true
		}
	}
	return false
}

/*检验结果是否正确*/
func check(i, n int) bool {
	//纵向
	if i-3 >= 0 {
		if searchFromNum(pos[i]+pos[i-3]) == false {
			return false
		}
	}
	//横向
	if i%3 != 0 {
		if searchFromNum(pos[i]+pos[i-1]) == false {
			return false
		}
	}
	return true
}

var down, up int = 0, 9

/*填入1~10到九宫格的解,回溯法*/
func fillBox(i, n, r int, count *int) {
	if i == n {
		(*count)++
		for i := 0; i < r; i++ {
			for j := 0; j < r; j++ {
				fmt.Printf("%3d", pos[i*r+j])
			}
			fmt.Println()
		}
		fmt.Println("============")
		return
	}
	for j := down; j <= up; j++ {
		//先放入
		pos[i] = sub[j]
		if sub[j] != -1 && check(i, n) {
			sub[j] = -1
			fillBox(i+1, n, r, count)
			sub[j] = pos[i]
		}

	}
}

func main() {
	count := 0
	fillBox(0, 9, 3, &count)
	fmt.Println(count)
}

解法二

package main

import (
	"fmt"
)

var pos [9]int
var sub []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var num []int = []int{1, 2, 3, 5, 7, 11, 13, 17, 19}

/*从质数中查找,找到返回true*/
func searchFromNum(n int) bool {
	for i := 0; i < 9; i++ {
		if n == num[i] {
			return true
		}
	}
	return false
}

/*检验结果是否正确*/
func check(n int) bool {
	//行相邻
	for i := 0; i < n; i++ {
		for j := 0; j < n-1; j++ {
			if searchFromNum(pos[i*n+j]+pos[i*n+j+1]) == false {
				return false
			}
		}
	}
	//列相邻
	for j := 0; j < n; j++ {
		for i := 0; i < n-1; i++ {
			if searchFromNum(pos[i*n+j]+pos[(i+1)*n+j]) == false {
				return false
			}
		}
	}
	return true
}

var down, up int = 0, 9

/*填入0~8到九宫格的解,全排列(枚举)*/
func fillBox(i, n, r int, count *int) {
	if i == n {
		if check(r) {
			(*count)++
			for i := 0; i < r; i++ {
				for j := 0; j < r; j++ {
					fmt.Printf("%3d", pos[i*r+j])
				}
				fmt.Println()
			}
			fmt.Println("============")
		}
		return
	}
	for j := down; j <= up; j++ {
		if sub[j] != -1 {
			pos[i] = sub[j]
			sub[j] = -1
			fillBox(i+1, n, r, count)
			sub[j] = pos[i]
		}

	}
}

func main() {
	count := 0
	fillBox(0, 9, 3, &count)
	fmt.Println(count)
}

回溯非递归解法:

package main

import (
	"fmt"
)

var num [9]int = [9]int{1, 2, 3, 5, 7, 11, 13, 17, 19}
var pos [9]int //存放连续的1~10
var sum, down, up, r int = 9, 0, 10, 3

func backTrack() int {
	sum--
	isNoConflict := true //默认true无冲突
	count := 0           //统计数0
	i := 0
	pos[0] = 1 //起始值是从1开始
	for {
		if isNoConflict {
			if i == sum {
				count++
				for i := 0; i < r; i++ {
					for j := 0; j < r; j++ {
						fmt.Printf("%3d", pos[i*r+j])
					}
					fmt.Println()
				}
				for pos[i] == up {
					i--
					if i == -1 {
						return count
					}
					//此时的i占据的值根本就没有参与check(),所以值是多少并不重要了,也就等于撤销了之前的占用
				}
				pos[i]++
			} else {
				i++
				pos[i] = 1 //起始值是从1开始
			}
		} else {
			//发生冲突
			for pos[i] == up {
				i--
				if i == -1 {
					return count
				}
				//此时的i占据的值根本就没有参与check(),所以值是多少并不重要了,也就等于撤销了之前的占用
			}
			pos[i]++
		}
		isNoConflict = check(i)
	}

}

func main() {
	fmt.Println(backTrack())
}

/*检验结果是否正确*/
func check(i int) bool {
	//搜索当前值是否已在前面使用过了
	for j := 0; j < i; j++ {
		if pos[j] == pos[i] {
			return false
		}
	}
	//纵向  
	if i-3 >= 0 {
		if searchFromNum(pos[i]+pos[i-3]) == false {
			return false
		}
	}
	//横向  
	if i%3 != 0 {
		if searchFromNum(pos[i]+pos[i-1]) == false {
			return false
		}
	}
	return true
}

/*从质数中查找,找到返回true*/
func searchFromNum(n int) bool {
	for i := 0; i < 9; i++ {
		if n == num[i] {
			return true
		}
	}
	return false
}

 

点赞