python数字位数重排_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

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

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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值