Python蓝桥杯练习 九宫重排


问题描述

  如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入格式

  输入第一行包含九宫的初态,第二行包含九宫的终态。

输出格式

  输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678.
123.46758

样例输出

3

样例输入

13524678.
46758123.

样例输出

22

思路

这是一个很典型的八数码问题,对于每种局面,他可以将空白向上、下、左、右四个方向移动,问题的关键在于向哪个方向移动会更接近目标(剪枝),我们可以根据曼哈顿距离或者不在位数来建立一个评估函数,那么我们优先趋向于处理评估函数值更小的那种方向,保留一个open表记录已经出现的局面,避免回头重复。
不在位数是指若对于一个格子,他的对应位置的数不是目标数,则加一
曼哈顿距离是指每个数要移动到目标位置需要移动的次数之和

Python源代码(这里的代码是一个在课堂上写的八数码的代码,不是这个题目的代码)

#8数码问题
import copy
import time

class status():
    # 以树的形式记录搜索
    # status是树的一个节点
    father=None

    def __init__(self,lst,evaluate,step):
        self.lst=lst # 记录当前状态
        self.evaluate=evaluate # 估价函数值
        self.step=step #递归树的深度

    def point_father(self,f):
        self.father=f # 指向父节点

    def change_evaluate(self,e):
        self.evaluate=e # 更新估价函数值

def compute_count_unpos(lst,b):
    # 不在位数
    count=0
    for i in range(9):
        if(lst[i]==0):
            continue
        if(lst[i]!=b.lst[i]):
            count+=1
    return count

def compute_manhadun_dis(lst,b):
    # 曼哈顿距离
    distance=0
    for i in range(9):
        if(lst[i]==0):
            continue
        des=0
        for j in range(9):
            if(lst[i]==b.lst[j]):
                des=j
                break
        hang1=des//3
        hang2=i//3
        des=des%3
        i=i%3
        distance+=(abs(hang1-hang2)+abs(des-i))
    return distance

def develop(now):
    # 通过移动空白位置,拓展现在的状态,生成移动后可能的状态并以列表形式返回
    out=[]

    temp = copy.deepcopy(now.lst)
    for i in range(len(temp)):
        if(temp[i]==0):
            pos = i

    #向上
    if(pos//3>0):#向上
        temp[pos],temp[pos-3]=temp[pos-3],temp[pos]
        out.append(temp)
    temp = copy.deepcopy(now.lst)
    if(pos//3<2):#向下
        temp[pos],temp[pos+3]=temp[pos+3],temp[pos]
        out.append(temp)
    temp = copy.deepcopy(now.lst)
    if(pos%3>0):#向左
        temp[pos],temp[pos-1]=temp[pos-1],temp[pos]
        out.append(temp)
    temp = copy.deepcopy(now.lst)
    if(pos%3<2):#向右
        temp[pos],temp[pos+1]=temp[pos+1],temp[pos]
        out.append(temp)

    global generate
    generate+=len(out)
    return out


def judge_exist(child,table):
    #判断child的list是否在table表中存在
    for i in range(len(table)):
        if(table[i].lst==child):
            return i
    return -1

def print_father(now):
    if(now.father==None):
        print(now.lst,now.evaluate)
        return
    print_father(now.father)
    print(now.lst,now.evaluate)


def main(compute_evaluate):
    global expand
    lst=[2,8,3,1,6,4,7,0,5] # 初始化状态
    b=status([1,2,3,8,0,4,7,6,5],0,0) # 目标状态
    a=status(lst,compute_evaluate(lst,b)+0,0) # 得到节点a

    open=[a] # 初始化open表
    closed=[]

    flag=0
    step=0 # 深度初始化为0
    while(len(open)!=0):
        now=open[0]
        open.remove(now) #取出open表的首节点
        if(now.lst==b.lst): #如果他是目标状态,则结束
            print("匹配成功!")
            print_father(now)
            flag=1
            break

        children=develop(now) #生成当前状态的后续状态
        if(len(children)==0): #无法生成,则取下个节点
            continue

        for child in children:
            if(judge_exist(child,open)==-1 and judge_exist(child,closed)==-1): #如果子状态不在open表或closed表
                evaluate=compute_evaluate(child,b)+now.step+1 #计算估价函数值
                node=status(child,evaluate,now.step+1) #成为新状态加入open表,深度+1
                node.point_father(now) #父节点指向now
                expand+=1
                open.append(node)

            elif(judge_exist(child,open)!=-1): #如果已经在open表
                index=judge_exist(child,open) #得到子节点在open表的下标
                if(now.step+1<open[index].step): #如果是沿着更短的深度到达
                    expand += 1
                    open[index].step=now.step+1 #更新子节点的估价函数和深度和父节点
                    open[index].point_father(now)
                    open[index].change_evaluate(compute_evaluate(child,b)+now.step+1)

            elif(judge_exist(child,closed)!=-1): #如果已经在closed表
                index = judge_exist(child, closed) #得到子节点在closed表的下标
                if(now.step+1<closed[index].step): #如果是沿着更短的深度到达
                    expand += 1
                    temp=closed[index] #从closed表中移除
                    closed.remove(temp)
                    temp.step=now.step+1 #并重新加入open表中
                    temp.change_evaluate(compute_evaluate(child,b)+now.step+1)
                    temp.point_father(now)
                    open.append(temp)

        closed.append(now) #closed表移除当前已处理节点
        open.sort(key=lambda x:x.evaluate,reverse=False) #按照估价函数值排序open表,第一个元素估价函数值最高

    if(flag==0): #open表为空且未到达目标节点
        print("匹配失败!")

print("------不在为数估计函数--------")
expand=0
generate=0
time1=time.time()
main(compute_count_unpos)
time2=time.time()
print("运行时间为:",time2-time1)
print("生成节点数为:",generate)
print("拓展节点数为:",expand)
print("------曼哈顿距离估价函数--------")
expand=0
generate=0
time1=time.time()
main(compute_manhadun_dis)
time2=time.time()
print("运行时间为:",time2-time1)
print("生成节点数为:",generate)
print("拓展节点数为:",expand)


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM