/*
基本思路:
模拟选择+bfs,每次选择一个字母往串里放,然后模拟消除,把结果放入到队列中。
这里需要定义状态,当前串和手中球
如果直接使用字符串来表示,空间不够用,需要状态压缩。
由于串是由5个字母组成的,长度最多是16,可以用6进制来表示,0表示无的意思。
最多用6^16 大概是10^14次方,再加上手中球5位,可以用uint64来表示一个状态。
*/
// 节点数据
type SingleObject interface{}
// 单链表节点
type SingleNode struct {
Data SingleObject
Next *SingleNode
}
// 单链表
type SingleList struct {
mutex *sync.RWMutex
Head *SingleNode
Tail *SingleNode
Size uint
}
// 初始化
func (list *SingleList) Init() {
list.Size = 0
list.Head = nil
list.Tail = nil
list.mutex = new(sync.RWMutex)
}
// 添加节点到链表尾部
func (list *SingleList) Append(node *SingleNode) bool {
if node == nil {
return false
}
list.mutex.Lock()
defer list.mutex.Unlock()
if list.Size == 0 {
list.Head = node
list.Tail = node
list.Size = 1
return true
}
tail := list.Tail
tail.Next = node
list.Tail = node
list.Size += 1
return true
}
// 插入节点到指定位置
func (list *SingleList) Insert(index uint, node *SingleNode) bool {
if node == nil {
return false
}
if index > list.Size {
return false
}
list.mutex.Lock()
defer list.mutex.Unlock()
if index == 0 {
node.Next = list.Head
list.Head = node
list.Size += 1
return true
}
var i uint
ptr := list.Head
for i = 1; i < index; i++ {
ptr = ptr.Next
}
next := ptr.Next
ptr.Next = node
node.Next = next
list.Size += 1
return true
}
// 删除指定位置的节点
func (list *SingleList) Delete(index uint) bool {
if list == nil || list.Size == 0 || index > list.Size-1 {
return false
}
list.mutex.Lock()
defer list.mutex.Unlock()
if index == 0 {
head := list.Head.Next
list.Head = head
if list.Size == 1 {
list.Tail = nil
}
list.Size -= 1
return true
}
ptr := list.Head
var i uint
for i = 1; i < index; i++ {
ptr = ptr.Next
}
next := ptr.Next
ptr.Next = next.Next
if index == list.Size-1 {
list.Tail = ptr
}
list.Size -= 1
return true
}
// 获取指定位置的节点,不存在则返回nil
func (list *SingleList) Get(index uint) *SingleNode {
if list == nil || list.Size == 0 || index > list.Size-1 {
return nil
}
list.mutex.RLock()
defer list.mutex.RUnlock()
if index == 0 {
return list.Head
}
node := list.Head
var i uint
for i = 0; i < index; i++ {
node = node.Next
}
return node
}
// 输出链表
func (list *SingleList) Display() {
if list == nil || list.Size == 0 {
fmt.Println("this single list is nil")
return
}
list.mutex.RLock()
defer list.mutex.RUnlock()
fmt.Printf("this single list size is %d \n", list.Size)
ptr := list.Head
var i uint
for i = 0; i < list.Size; i++ {
fmt.Printf("No%3d data is %v\n", i+1, ptr.Data)
ptr = ptr.Next
}
}
// Queue 队列信息
type Queue struct {
list *SingleList
}
// Init 队列初始化
func (q *Queue) Init() {
q.list = new(SingleList)
q.list.Init()
}
// Size 获取队列长度
func (q *Queue) Size() uint {
return q.list.Size
}
// Enqueue 进入队列
func (q *Queue) Enqueue(data interface{}) bool {
return q.list.Append(&SingleNode{Data: data})
}
// Dequeue 出列
func (q *Queue) Dequeue() interface{} {
node := q.list.Get(0)
if node == nil {
return nil
}
q.list.Delete(0)
return node.Data
}
// Peek 查看队头信息
func (q *Queue) Peek() interface{} {
node := q.list.Get(0)
if node == nil {
return nil
}
return node.Data
}
type Node struct {
Val int
Children []*Node
}
var A2Num = map[rune]int{'R':1,'Y':2, 'B':3, 'G':4, 'W':5}
var Base uint64 = 6
var Hand uint64 = 10000
// 使用6进制转化。
func getHash(arr []int) uint64 {
var sum uint64 = 0
for _, value := range arr {
sum *=Base
sum+=uint64(value)
}
return sum
}
// 6进制反序列化。
func getArrFromHash(h uint64 ) []int{
if h==0 {return nil}
arr := make([]int, 0)
for ;h>0;h/=Base {
arr = append(arr, int(h%Base))
}
return arr
}
// 将串和手中球进行编码成一个状态。
func encode(board , hand []int) uint64 {
sort.Ints(hand)
boardHash, handHash := getHash(board), getHash(hand)
return boardHash*Hand+handHash
}
// 解码状态。
func decode(code uint64) (board ,hand[]int) {
boardHash, handHash := code/Hand , code%Hand
return getArrFromHash(boardHash), getArrFromHash(handHash)
}
// 模拟添加一个球后的串,要考虑消除。
func insert(arr []int, position, ball int) []int {
curball := ball
cnt,i,j:=1, position-1,position
for ;; { // 从插入的位置开始消除,其他位置一开始肯定不满足消除的情况。
ii,jj:=i,j
for ;ii>=0 && arr[ii]==curball ;ii-- {
cnt++
}
for ;jj<len(arr) && arr[jj]==curball ;jj++ {
cnt++
}
if cnt>=3 { // 需要消除
i,j = ii,jj
curball = -1
cnt=0
if i>=0 {
curball = arr[i]
}else if j<len(arr){
curball = arr[j]
}
} else { // 不消除了直接退出
break
}
}
newArr := []int{}
if i>=0 {
newArr = append(newArr, arr[:i+1]...)
}
if j==position{ // 说明一次也没有消除过手中球需要添加
newArr = append(newArr, ball)
}
if j<len(arr){
newArr = append(newArr, arr[j:]...)
}
return newArr
}
func getArr(s string) []int {
if len(s)==0 {return nil}
arr := make([]int, len(s))
for i, value := range s {
arr[i]=A2Num[value]
}
return arr
}
func makeStartCode(board , hand string) uint64{
boardArr := getArr(board)
handArr := getArr(hand)
return encode(boardArr, handArr)
}
/*
基本思路:
模拟选择+bfs,每次选择一个字母往串里放,然后模拟消除,把结果放入到队列中。
这里需要定义状态,当前串和手中球
如果直接使用字符串来表示,空间不够用,需要状态压缩。
由于串是由5个字母组成的,长度最多是16,可以用6进制来表示,0表示无的意思。
最多用6^16 大概是10^14次方,再加上手中球5位,可以用uint64来表示一个状态。
*/
func findMinStep(board , hand string) int {
step := 0
q := &Queue{}
q.Init()
start := makeStartCode(board, hand)
q.Enqueue(start)
vis := make(map[uint64]bool) // 标记是否已经搜索过
for q.Size()>0 {
for l:=q.Size();l>0;l-- {
front, ok := q.Dequeue().(uint64)
if !ok {continue}
chain, handBall := decode(front)
if len(chain)==0 { // 已经消除完毕
return step
}
for i, _ := range chain {
for j, ballColor := range handBall {
if ballColor==0{continue} // 已经被选择了
if j>0 && handBall[j-1]==ballColor {continue} // 跟之前做了同样的选择,没有意义
if i>0 && chain[i-1]==ballColor {continue} // 填在右边不需要做
/*
"RRWWRRBBRR"
"WB" 这个用例证明下面的判断逻辑是不对的
*/
//if color!=ballColor {continue} //希望放在当前的左边,但颜色不一样,放了也没用。(该逻辑不正确)
// 在当前位置左边放一个
newChain := insert(chain, i, ballColor)
copyHandBall := make([]int, len(handBall))
copy(copyHandBall, handBall)
copyHandBall[j]=0 //置为已选择
newState := encode(newChain, copyHandBall)
if !vis[newState] { //未搜索过
vis[newState] = true
q.Enqueue(newState) // 加入队列
}
}
}
}
step++
}
return -1
}