當前位置:吉日网官网 - 紀念幣收藏 - 九宮格謎題~ ~找到解決這個問題的方法~思路~代碼都可以~ ~是關於它的歸約算法~在線~謝謝。

九宮格謎題~ ~找到解決這個問題的方法~思路~代碼都可以~ ~是關於它的歸約算法~在線~謝謝。

/u/8780/showart.php?id=163291

在壹個3×3的九宮中,有八個數字1-8和壹個空格隨機放置在網格中,如圖1-1。現在要求認識到這個問題:調整成圖1-1右圖所示的形式。調整規則是壹次只能將壹個與空格相鄰的數字(上、下、左或右)轉換到空格中。嘗試編程解決這個問題。

(圖1-1)

二、題目分析:

這是人工智能中的經典問題之壹。問題是在壹個3×3的棋盤裏,放了8個方格,剩下的都是空的,每壹步棋只能和相鄰的空格交換。程序自動生成問題的初始狀態,並通過壹系列交換動作將其轉化為目標排列(如下圖1-2至圖1-3)。

(圖1-2)(圖1-3)

在這個問題中,程序生成的隨機排列轉化為目標* * *,有兩種可能,且兩者不能同時成立,即奇排列和偶排列。隨機排列的數組可以用從左到右、從上到下的壹維數組來表示。如上圖所示,1-2可以表示為{8,7,1,5,2,6,3,4,0}其中0表示壹個空格。

在這個數組中,我們首先計算它可以重新排列的結果,公式是:

∑ (F(X)) = y,其中F(X)

是壹個數前面小於這個數的數,當y是奇數和偶數時有解。(8)確定是否存在數字問題的解決方案)

上面的數組可以解決它的結果。

f(8)= 0;

f(7)= 0;

f(1)= 0;

f(5)= 1;

f(2)= 1;

f(6)= 3;

f(3)= 2;

f(4)= 3;

y = 0+0+0+1+1+3+2+3 = 10

Y = 10是偶數,所以它的重排是如圖1-3所示的結果。如果求和結果是奇數重排,結果是最右邊的排列,如圖1-1。

三、算法分析

解決方法是交換空格(0)的位置,直到到達目標位置。圖形表示為:

(圖3-1)

想要得到最好的,需要使用廣度優先搜索,所以九宮有9個!排列方式有362880種,數據量非常大。使用廣度搜索,需要記住每個節點的排列形式。如果用數組來記錄,會占用大量內存,可以適當壓縮數據。以DWORD格式保存。壓縮後的形式是每個數用3位表示,也就是3× 9 = 27字節。因為8的二進制表示是1000,所以不能用3位表示。壹個竅門是把8表示成000,然後用多出來的五個字來表示8的位置,所以可以用DWORD來表示。用移位和或運算壹個壹個移動數據,比乘法快。定義了幾個結果來存儲遍歷的結果和搜索完成後的最優路徑。

該類的結構如下:

分類網格

{

公共:

結構位置列表

{

德沃德廣場;

PlaceList * Left

PlaceList * Right

};

struct Scanbuf

{

德沃德廣場;

int ScanID

};

結構路徑列表

{

無符號字符路徑[9];

};

私人:

PlaceList * m _ pPlaceList

Scanbuf * m _ pScanbuf

RECT m _ rResetButton;

RECT m _勞托巴頓;

公共:

int m _ iPathsize

clock _ t m _ iTime

UINT m _ iStepCount

無符號字符m _ iTargetChess[9];

無符號字符m _ I chess[9];

HWND m _ hClientWin

PathList * m _ pPathList

波爾m _博托倫;

私人:

內嵌bool AddTree(DWORD place,PlaceList * & amp家長);

void free tree(place list * & amp;家長);

內聯void ArrayToDword(無符號字符數組,DWORD & amp數據);

inline void DwordToArray(DWORD數據,無符號char * array);

inline bool MoveChess(無符號char *array,int way);

bool EstimateUncoil(無符號char * array);

void GetPath(單位深度);

公共:

void move chess(int way);

bool ComputeFeel();

void active shaw(HWND h view);

void draw grid(HDC HDC,RECT client rect);

void DrawChess(HDC hDC,RECT client rect);

void Reset();

void按鈕(點pnt,HWND h view);

公共:

cninegrid();

~ cninegrid();

};

用向量模板計算隨機的隨機數組,用random_shuffle(,)函數對數組數據進行置亂,計算出目標結果。代碼:

void cn inegrid::Reset()

{

if(m_bAutoRun)返回;

向量vs;

int I;

for(I = 1;我& lt9 ;i ++)

vs.push_back(壹);

vs . push _ back(0);

random_shuffle(vs.begin()、vs . end());

random_shuffle(vs.begin()、vs . end());

for(I = 0;我& lt9 ;i ++)

{

m _ iChess[I]= vs[I];

}

如果(!估計解卷(m_iChess))

{

無符號字符數組[9] = {1,2,3,8,0,4,7,6,5 };

memcpy(m_iTargetChess,array,9);

}

其他

{

無符號字符數組[9] = {1,2,3,4,5,6,7,8,0 };

memcpy(m_iTargetChess,array,9);

}

m _ iStepCount = 0;

}

數據壓縮功能的實現:

inline void cninegrid::ArrayToDword(unsigned char * array,DWORD & amp數據)

{

無符號字符夜= 0;

for(int I = 0;我& lt9 ;i ++)

{

if (array[i] == 8)

{

night =(無符號char)I;

打破;

}

}

array[night]= 0;

數據= 0;

data =(DWORD)((DWORD)array[0]& lt;& lt29 |(DWORD)array[1]& lt;& lt26 |

(DWORD)數組[2]& lt;& lt23 |(DWORD)array[3]& lt;& lt20 |

(DWORD)數組[4]& lt;& lt17 |(DWORD)array[5]& lt;& lt14 |

(DWORD)數組[6]& lt;& lt11 |(DWORD)array[7]& lt;& lt8 |

(DWORD)數組[8]& lt;& lt5 |夜);

array[night]= 8;

}

解壓和壓縮正好相反。解壓縮代碼:

內聯void cninegrid::DwordToArray(DWORD數據,無符號字符*數組)

{

無符號字符chtem

for(int I = 0;我& lt9 ;i ++)

{

chtem =(無符號字符)(data & gt& gt(32-(I+1)* 3);0x 000000007);

array[I]= chtem;

}

chtem =(無符號字符)(data & amp0x 0000001F);

array[chtem]= 8;

}

由於可擴展數據量非常大,保存時使用DWORD類型,每壹步數據都記錄在壹棵排序的二叉樹中,從左到右從小到大排列,搜索時間比每次近萬次快了差不多n倍。循環中使用的幾個函數被聲明為內聯函數,同時搜索插入的數據在樹中是否會重復,以加快整體速度。二叉樹插入碼:

AddTree(DWORD place,PlaceList * & amp父母)

{

if (parent == NULL)

{

parent = new place list();

父->;left = parent->;右=空;

父->;Place =地點;

返回true

}

if(父-& gt;Place == place

返回false

if(父-& gt;Place & gt地點)

{

返回AddTree(place,parent-& gt;對);

}

返回AddTree(place,parent-& gt;左);

}

計算結果是奇數碼還是偶數碼:

bool CNI negrid::estimate dependicle(無符號字符*數組)

{

int sun = 0;

for(int I = 0;我& lt8 ;i ++)

{

for(int j = 0;j & lt9 ;j ++)

{

if (array[j]!= 0)

{

if (array[j] == i +1)

打破;

if(array[j]& lt;i + 1)

sun++;

}

}

}

if (sun % 2 == 0)

返回true

其他

返回false

}

移動到空格位的代碼比較簡單,只需要計算是否會移動到框外,移動時計算是否已經是目標結果,用來給用戶手動移動和提示。代碼:

內聯bool cninegrid::move chess(無符號char *array,int way)

{

int零,chang

bool moveok = false

for(零= 0;zero & lt9 ;零++)

{

if(數組[零] == 0)

打破;

}

點pnt

pnt.x =零% 3;

pnt.y = int(零/3);

開關(路)

{

案例0://上

if(pnt . y+1 & lt;3)

{

Chang =(pnt . y+1)* 3+pnt . x;

array[zero]= array[Chang];

array[Chang]= 0;

moveok = true

}

打破;

案例1 : //down

if(pnt . y-1 & gt;-1)

{

Chang =(pnt . y-1)* 3+pnt . x;

array[zero]= array[Chang];

array[Chang]= 0;

moveok = true

}

打破;

案例2://左

if(pnt . x+1 & lt;3)

{

Chang = pnt . y * 3+pnt . x+1;

array[zero]= array[Chang];

array[Chang]= 0;

moveok = true

}

打破;

案例3://對

if(pnt . x-1 & gt;-1)

{

Chang = pnt . y * 3+pnt . x-1;

array[zero]= array[Chang];

array[Chang]= 0;

moveok = true

}

打破;

}

if(moveok & amp;& amp!m _包托倫)

{

m _ istepcount++;

DWORD temp1,temp2

ArrayToDword(數組,temp 1);

ArrayToDword(m_iTargetChess,temp 2);

if (temp1 == temp2)

{

MessageBox(NULL,“妳真聰明,這麽快就完成了!”, "^_^" , 0);

}

}

返回moveok

}

在廣度搜索中,父節點所在的數組索引被記錄在子節點中,因此在得到目標排列時,可以從子節點開始反向搜索,得到最優的搜索路徑。使用變量m_iPathsize記錄總步數。具體的功能代碼是:

void cn inegrid::get path(單位深度)

{

int now = 0,maxpos = 100;

UINT parentid

if (m_pPathList!=空)

{

刪除[]m _ ppath list;

}

m_pPathList =新路徑列表[max pos];

parentid = m _ pScanbuf[深度]。ScanID

DwordToArray(m _ pScanbuf[深度])。Place,m_pPathList[++now]。路徑);

while(parentid!= -1)

{

if (now == maxpos)

{

maxpos+= 10;

path list * tem list = new path list[max pos];

memcpy(temlist,m_pPathList,sizeof(path list)*(max pos-10));

刪除[]m _ ppath list;

m _ pPathList = temlist

}

DwordToArray(m _ pScanbuf[parentid])。Place,m_pPathList[++now]。路徑);

parentid = m_pScanbuf[parentid]。ScanID

}

m _ iPathsize = now

}

動態排列的演示功能最簡單。為了給主窗體壹個及時刷新的機會,啟動了壹個線程。當主窗體需要刷新時,只需使用Slee(UINT)函數暫停線程即可。代碼:

無符號_ _ stdcall MoveChessThread(LPVOID PPAR am)

{

cninegrid * p grid =(cninegrid *)PP aram;

RECT矩形;

p gird-& gt;m _ iStepCount = 0;

* GetClientRect(pg ird-& gt;m _ hClientWin & amp;rect);

for(int I = p gird-& gt;m _ iPathsize我& gt0 ;我-)

{

memcpy(pg ird-& gt;m_iChess,pg ird-& gt;m _運動員[i]。路徑,9);

p gird-& gt;m _ istepcount++;

invalidator(pg ird-& gt;m _ hClientWin & amp;rect,false);

睡眠(300);

}

char msg[100];

sprintf(msg,“^_^!搞定了!\ r \計算步驟需要%d毫秒",p grid-& gt;m _ iTime);

MessageBox(NULL,msg," ~_~ ",0);

p gird-& gt;m _ bAutoRun = false

返回0L;

}

最後介紹了搜索函數的原理。首先,獲取源數組,並將其轉換為DWORD類型。與目標比較,如果同樣的完成不壹樣,交換數據和空間位置,加入二叉樹,搜索下壹個結果,直到沒有下壹步可走。這樣,在找到目標結果之前,函數:

bool cninegrid::ComputeFeel()

{

無符號char * array = m _ iChess

UINT I;

const int MAXSIZE = 362880

無符號字符temparray[9];

DWORD target,fountain,parentID = 0,child = 1;

ArrayToDword(m_iTargetChess,target);

ArrayToDword(數組,噴泉);

if(噴泉==目標)

{

返回false

}

if (m_pScanbuf!=空)

{

delete[]m _ pScanbuf;

}

m _ pScanbuf = new Scanbuf[MAXSIZE];

AddTree(fountain,m _ pplace list);

m_pScanbuf[ 0 ]。場所=噴泉;

m_pScanbuf[ 0 ]。ScanID =-1;

clock _ t Tim = clock();

while(parentID & lt;MAXSIZE & amp& ampchild & ltMAXSIZE)

{

parent = m_pScanbuf[parentID]。地方;

for(I = 0;我& lt4 ;i ++) // 0:上,1:下,2:左,3:右

{

DwordToArray(parent,temparray);

If (MoveChess(temparray,i)) //移動成功了嗎?

{

ArrayToDword(temparray,fountain);

If (add tree (fountain,m _ pplace list))//添加搜索次數。

{

m_pScanbuf[ child ]。場所=噴泉;

m_pScanbuf[ child ]。ScanID = parentID

If (fountain == target) //找到結果了嗎?

{

m _ iTime = clock()-Tim;

get path(child);//計算路徑

FreeTree(m _ pplace list);

delete[]m _ pScanbuf;

m _ pScanbuf = NULL

返回true

}

child++;

}

}

} //對於I

parentid++;

}

m _ iTime = clock()-Tim;

FreeTree(m _ pplace list);

delete[]m _ pScanbuf;

m _ pScanbuf = NULL

返回false

}

重要功能介紹完畢;以下是程序的運行結果和操作結果:

  • 上一篇:中國詩歌史上第壹首真正意義上的詩是誰寫的?20世紀30年代的《綠之歌》是誰的作品?
  • 下一篇:人參海豹丸的功效和作用,人參海豹丸和海豹參壹樣嗎?
  • copyright 2024吉日网官网