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