1. 程式人生 > >八數碼難題(啟發式搜尋)

八數碼難題(啟發式搜尋)

八數碼難題---啟發式搜素

1.啟發式搜尋:

特點:重排OPEN表,選擇最有希望的節點加以擴充套件

種類:有序搜尋(A演算法)、A*演算法等

2.估價函式

用來估算節點處於最佳求解路徑上的希望程度的函式

f(n) = g(n) + h(n)

 n——搜尋圖中的某個當前被擴充套件的節點;

f(n) ——從初始狀態節點s, 經由節點n到達目標節點ng,估計的最小路徑代價;

g(n) ——從s到n 的實際路徑代價;

h(n)——從n到ng,估計的最小路徑代價。

 八數碼難題估價函式:f(n)=d(n)+w(n)                     

其中:d(n)為n的深度           w(n)為不在位的棋子數

3.有序搜尋:

選擇OPEN表上具有最小f值的節點作為下一個要擴充套件的節點。

八數碼難題使用全域性擇優搜尋:

   選擇OPEN表上具有最小f值的節點作為下一個要擴充套件的節點,即總是選擇最有希望的節點作為下一個要擴充套件的節點。

在八數碼難題中, 令估價函式

       f(n)=d(n)+p(n)

啟發函式h(n)=p(n),p(n)為不在位的棋子與其目標位置的距離之和,則有p(n)≤h*(n),滿足A*演算法的限制條件。

w(n)——不在位的棋子數,不夠貼切,錯誤選用節點加以擴充套件。

p(n)——更接近於h*(n)的h(n),其值是節點n與目標狀態節點相比較,每個錯位棋子在假設不受阻攔的情況下,移動到目標狀態相應位置所需走步的總和。

p(n)比w(n)更接近於h*(n),因為p(n)不僅考慮了錯位因素,還考慮了錯位的距離(移動次數)

資料結構的建立:由於open,close表常設計刪除增加節點的操作,故使用單鏈表最好

open表儲存生成的節點且未被處理的節點,處理過的節點取出放入close表

close表儲存處理過的節點

status表儲存所有生成的節點

採用全域性擇優的思想

java程式碼如下:
package 八數碼難題;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;





public class Main {
	static int f[][]=new int[3][3];
	static int e[][]=new int[3][3];
	static int p=0;
	static int dir[][]={{0,1},{1,0},{0,-1},{-1,0}};
	static List<node>openlist=new LinkedList<node>();
	static List<node>closelist=new LinkedList<node>();
	static int d=0;
	static List<node>status=new LinkedList<node>();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String str1;//初始狀態
		String str2;//目標狀態
		str1="2831647.5";//空格用.表示
		str2="1238.4765";
		for(int i=0;i<str1.length();i++){
			f[i/3][i-i/3*3]=str1.charAt(i)=='.'?0:str1.charAt(i)-'0';
			e[i/3][i-i/3*3]=str2.charAt(i)=='.'?0:str2.charAt(i)-'0';
		}
		node f1=new node(f,d,e);
		openlist.add(f1);
		status.add(f1);
		Search();//尋找最短路徑
		toPrint();//輸出狀態空間
	}
	public static boolean check(int x,int y){
		if(x<0||x>=3||y<0||y>=3){
			return false;
		}
		return true;
	}
	public static boolean contains(List<node> list,int a[][]){
		int flag=0;
		int a1[][] = new int[3][3];
		
		for(int i=0;i<list.size();i++){
			flag=0;
			for(int j=0;j<3;j++){
				a1[j]=list.get(i).a[j].clone();
				for(int k=0;k<3;k++){
					if(a1[j][k]==a[j][k]){
						flag++;
					}
				}
			}
			if(flag==9) return false;
		}
		return true;
	}
	//輸出狀態空間
	public static void toPrint(){
		int a[][];
		System.out.println();
		System.out.println("========狀態空間大小為:"+status.size()+"-----");
		int k1=0,i=0;
		while(i<status.size()){
			a=status.get(i).a;
			System.out.println("---------------");
			for(int j=0;j<3;j++){
				for(int k=0;k<3;k++){
					System.out.print(a[j][k]+" ");
				}
				System.out.println();
			}
			i++;
		}
	}
	public static void Search(){
		hnode ehn=new hnode(e);
		while(!openlist.isEmpty()){
			int at[][]=new int[3][3];
			int min=0;
			for(int i=0;i<openlist.size();i++){
				if(openlist.get(i).f<openlist.get(min).f){
					min=i;
				}
			}
			node n=null;
			n=openlist.remove(min);
			closelist.add(n);
			
			
			for(int i=0;i<3;i++){
				at[i]=n.a[i].clone();
			}
			
			int x = 0,y = 0;
			for(int i=0;i<3;i++){
				for(int j=0;j<3;j++){
					if(at[i][j]==p){
						x=i;y=j;//記錄空格在棋盤中的位置(原)
						break;
					}
				}
			}
			for(int i=0;i<4;i++){
				int nx,ny,t;
				nx=x+dir[i][0];//空格移動的位置(現)
				ny=y+dir[i][1];
				if(check(nx,ny)){
					t=at[nx][ny];
					at[nx][ny]=at[x][y];
					at[x][y]=t;
					d=n.h+1;
					hnode thn=new hnode(at);
					node tn=new node(at,d,e);
					if(thn.equals(ehn)){
						//找到最短路徑
						status.add(tn);
						closelist.add(tn);
						toOutput();
						return;
					}
					
					if(contains(openlist,at)&&contains(closelist,at)){
						openlist.add(tn);
						status.add(tn);
					}
					t=at[nx][ny];
					at[nx][ny]=at[x][y];
					at[x][y]=t;
				}
			}
		}
		return;
	}
	public static void toOutput(){
		System.out.println("====移動路徑如下====");
		System.out.println("所需步數為:"+d);
		for(int i=0;i<closelist.size();i++){
			int a[][]=closelist.get(i).a;
			System.out.println("----------------------");
			for(int j=0;j<3;j++){
				for(int k=0;k<3;k++){
					System.out.print(a[j][k]+" ");
				}
				System.out.println();
			}
		}
	}

}



class hnode{
	//hash用於hashSet
	int hash;
	public hnode(){
		
	}
	public hnode(int a[][]){
		int hc=0;
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				hc*=10;
				hc+=a[i][j];
			}
		}
		this.hash=hc;
	}
	public boolean equals(Object object){
		hnode thn=(hnode)object;
		return thn.hash==hash;
	}
	@Override
	public int hashCode(){
		return hash;
	}
	
	
}
class node{
	int a[][];//九宮格的狀態
	int h;//深度
	int f;//從初始狀態節點s, 經由節點n到達目標節點ng,估計的最小路徑代價
	int p;//不在位的棋子與其目標位置的距離之和
	public node(int [][]a,int h,int [][]e){
		this.a=new int[3][];
		for(int i=0;i<3;i++){
			this.a[i]=a[i].clone();
		}
		this.h=h;
		this.p=work(e);
		this.f=this.h+this.p;
	}
	public int work(int e[][]){
		int ants=0;
		for(int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				if(a[i][j]!=e[i][j]&&a[i][j]!=0){
					int r=0,c = 0,flag=0;
					for(int k=0;k<3;k++){
						for(int k1=0;k1<3;k1++){
							if(a[i][j]==e[k][k1]){
								r=k;
								c=k1;
								ants+=Math.abs(r-i);
								ants+=Math.abs(c-j);
								flag=1;
								break;
							}
						}
						if(flag==1) break;
					}
					
				}
			}
		}
		return ants;
	}
}

相關推薦

數碼難題啟發式搜尋

八數碼難題---啟發式搜素1.啟發式搜尋:特點:重排OPEN表,選擇最有希望的節點加以擴充套件種類:有序搜尋(A演算法)、A*演算法等2.估價函式用來估算節點處於最佳求解路徑上的希望程度的函式f(n) = g(n) + h(n) n——搜尋圖中的某個當前被擴充套件的節點;f(

路勁尋找-數碼問題判重

題目:編號為1~8的8個正方形滑塊被擺成3行3列(有一個格子留空)。把一種狀態變成另一種狀態最少移動多少步,到達不了,就輸出-1。 2 6 4 1 3 7  

【 HDU1043-經典BFS+康拓展開 數碼待更

給定一個序列,由1~8數字和字母x組成,表示的是一個3*3的矩形。每次操作x都能與相鄰的數字交換,問如何操作才能使得序列為{1,2,3,4,5,6,7,8,x}。   //多組資料-需要計算全部路徑後直接輸出 //反向搜尋+打表(離線) #include<iostream&

對於A*演算法的學習啟發式搜尋

開篇 這篇文章介紹找最短路徑的一種演算法,它的字我比較喜歡:啟發式搜尋。 標題上寫的是翻譯,只是覺得原文講解的思路很清晰。這篇文章整體構思和原文相差不多,只是有些地方有小的改動, 我想的是用更容易理解的方式、更簡潔的把A*演算法的思想呈現出來。 文章中出現的詞openli

A*演算法解決數碼問題C++版本

八數碼問題定義: 八數碼問題也稱為九宮問題。在3×3的棋盤,擺有八個棋子,每個棋子上標有1至8的某一數字,不同棋子上標的數字不相同。棋盤上還有一個空格,與空格相鄰的棋子可以移到空格中。要求解決的問題是:給出一個初始狀態和一個目標狀態,找出一種從初始轉變成

啟發式搜尋演算法求解數碼問題C

下午看一個遊戲的演算法時看了一下啟發式搜尋演算法,心血來潮跑了一遍很久很久以前寫八數碼的程式(C語言),發現各種問題,後來順著思路整理了一下,貼出來和大家分享一下,直接上程式碼: // // main.c // yunsuan // // Created by ma

P1379 數碼難題-啟發式搜尋

P1379 八數碼難題 題意:在3×3的棋盤上,擺有八個棋子,每個棋子上標有1至8的某一數字。棋盤中留有一個空格,空格用0來表示。空格周圍的棋子可以移到空格中。要求解的問題是:給出一種初始佈局(初始狀態)和目標佈局(為了使題目簡單,設目標狀態為123804765),找到一種最少

BZOJ1975 SDOI2010魔法豬學院啟發式搜尋+最短路+堆

  對反圖跑最短路求出每個點到終點的最短路徑,令其為估價函式大力A*,第k次到達某個點即是找到了到達該點的非嚴格第k短路,因為估價函式總是不大於實際值。bzoj可能需要手寫堆。正解是可持久化可並堆,至今是第二次見到這個那當然是不學啦。 #include<iostream> #includ

數碼問題劉汝佳版

state const r++ return 可用 八數碼 ext ear ans 可以采用dfs,對空白點進行操作,然後可用編碼法,哈希表或者集合來標記,代碼如下 #include<iostream> #include<algorithm> #i

HDU-1043:Eight數碼+bfs反向或A*

題目大意: 給你一個3*3的表,中間有0到8  9個數字。每次你可以使用0和其相鄰的數字交換。使得最後得到一種題目要求的狀態並求出最短路徑。 解題思路: 當然想到的就是bfs求最短路徑,但是要解決幾個問題,用什麼存當前的狀態,map會超時,所以要用hash,hash可

紫書—數碼問題BFS

sed 計算 cstring 藍橋杯 需要 amp 代碼 scan size 八數碼問題 編號為1~8的8個正方形滑塊被擺成3行3列(有一個格子空留),如圖所示。每次可以把與空格相鄰的滑塊(有公共邊才算相鄰)移到空格中,而它原來的位置就稱為了 新的空格。給定

luoguP1379 數碼難題[IDA*]

-i 感謝 ida har 初始 text can int 夢裏 題目描述 在3×3的棋盤上,擺有八個棋子,每個棋子上標有1至8的某一數字。棋盤中留有一個空格,空格用0來表示。空格周圍的棋子可以移到空格中。要求解的問題是:給出一種初始布局(初始狀態)和目標布局

[ACM] hdu 1251 統計難題 字典樹

第一次 stdio.h scrip null 明顯 output 代碼 ane 處理 統計難題 Problem Description Ignatius近期遇到一個難題,老師交給他非常多單詞(僅僅有小寫字母組成,不會有反復的單詞出現),如今老師要他統計出以某

HDU 2045 LELE的RPG難題遞推

%d out miss rpg 方式 最終 desc ont != 不容易系列之(3)—— LELE的RPG難題 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768

【基礎練習】【BFS+A*】codevs1225數碼難題題解

一點 說明 優先 data- push 練習 bool csdn tarjan 題目描寫敘述 Description Yours和zero在研究A*啟示式算法.拿到一道經典的A*問題,可是他們不會做,請你幫他們. 問題描寫敘述 在3×3的棋

洛谷 P1379 數碼難題

blank swa scan blog times tps dfs cst target 題目描述 在3×3的棋盤上,擺有八個棋子,每個棋子上標有1至8的某一數字。棋盤中留有一個空格,空格用0來表示。空格周圍的棋子可以移到空格中。要求解

luogu P1379 數碼難題

測試數據 ret %d -c opp ron line star 數碼 題目描述 在3×3的棋盤上,擺有八個棋子,每個棋子上標有1至8的某一數字。棋盤中留有一個空格,空格用0來表示。空格周圍的棋子可以移到空格中。要求解的問

次會議5.6

需求 5.6 計劃 展示 地圖 基本 人員 做出 修改 人員:劉、宋、房、張(馮因傷病缺席) 地點:地下室 主要議題:   1.分享了上周學習成果   2.討論並解決上周開發問題   3.安排下周開發計劃 會議結果:   1.房展示了對王者榮耀遊戲的人物的屬性比值轉換表,可

數碼難題

思路 效率 全排列 hash 超時 但是 做到 利用 排列 本題有寫法好幾個寫法,但主要思路是BFS: No。1 采用雙向寬搜,分別從起始態和結束態進行寬搜,暴力判重。如果只進行單向會超時。 No。2 采用hash進行判重,寬搜采用單向就可以AC。 No。3 運用康拓展開進

HDU1251 統計難題字典樹

lag != tac NPU math def tput struct bee HDU1251 統計難題 Ignatius最近遇到一個難題,老師交給他很多單詞(只有小寫字母組成,不會有重復的單詞出現),現在老師要他統計出以某個字符串為前綴的單詞數量(單詞本身也是自己的前綴)