用bfs 康托展開解決3 3數字華容道問題

2021-09-26 22:34:23 字數 2082 閱讀 5400

數字華容道的規則是這樣的:給出乙個亂序矩陣,你每次只能將數字0與其相鄰的數字交換位置,通過一系列交換,將矩陣排成有序的形式(見下圖,源自博主編的小遊戲)。

那麼,有沒有一種方法,能夠求出該問題的最優解(最少交換次數)呢?

我們不妨嘗試下最暴力的方法:「搜尋」。由於每次交換消耗的代價相等(均為1步),因此我們可以使用寬度優先搜尋(bfs)

要確定bfs所需的佇列空間大小,首先得計算在該問題中可能出現多少種「狀態」。

我們不妨將矩陣看成乙個字串,那麼由排列的知識,我們可以顯然地得到其總狀態數為

如此大量的狀態數,如果用普通的列舉判重,時間複雜度為

所以我們應當優化狀態判重的方法,由於所有狀態構成了9個數字全排列的所有情況,我們可以利用康托展開給狀態編號。

康托展開可以將每種狀態壓縮成其唯一對應的數字,從而可以通過完美雜湊判重,其公式是:

int f(int now)

return tot;

}

那麼,在判重時只需要在雜湊表中判斷其對應的狀態是否出現過即可。

分別向上下左右交換達成新的狀態,這個就比較簡單了。

void move_up()

return;

}void move_down()

return;

}void move_left()

return;

}void move_right()

return;

}

目標狀態轉換成字串後其實就是:「1234567890」,通過康托展開可以求得其對應的編碼為46233,那麼每次出現新狀態,直接判斷其是否一致即可。

另外,在寬搜中,首先達到目標狀態的,一定為最優解。因此只要達到目標狀態,便可直接退出程式。

bool check() //判斷是否達到目標狀態 

我們可以用乙個陣列last表示佇列中某個狀態是由哪乙個狀態轉移而來,那麼只要從目標狀態回溯一遍,即可求出完整方案。(此處**略)

#include#include#includeusing namespace std;

int head,tail;

int cantor[10];

int step[362881];

int location[362881];

char a[4][4];

char que[362881][10];

bool vis[362881];

int f(int now)

return tot;

}void move_up()

return;

}void move_down()

return;

}void move_left()

return;

}void move_right()

return;

}void first()

bool check() //判斷是否達到目標狀態

void print()

printf("\n");

return;

}int main()

first();

head=1;

tail=1;

vis[f(1)]=true;

if (check())

while (head<=tail)

move_down();

if (check())

move_left();

if (check())

move_right();

if (check())

head++;

} printf("no solution");

return 0;

}

康托展開和康托逆展開解決第K個排列問題

集合包含了 n!種不同的排列,將這n!種排列從小到大進行排序,某個排列為第k 個,求k時,使用康托展開,已知k求對應的排列時,使用康托逆展開。對於某個排列 a1,a2,a3,an 將其存入陣列s 求它在n!個排列中的位置k,用rli表示ai後面比ai小的數的個數,ri表示ai後面的數的個數,k 1 ...

loj 1165 bfs 康托展開)

思路 題目意思很簡單,就是通過一些位置的交換,最後變成有序數列,對於一組序列,我們可以用康托展開然後hash判重。然後就是普通的bfs,稍微留意一下細節即可。1 include2 include3 include4 include5 include6 using namespace std 7int...

HDU 1043 Eight(康托展開 BFS)

八數碼問題,輸入9個字元代表乙個3 3的棋盤,對於能還原的輸入,輸出一段包含 u d l r 操作的操作序列,不能還原的輸入輸出 unsolvable 輸入有多組,所以考慮先預處理出所有情況的操作序列,再根據輸入進行輸出 因為要遍歷所有情況,所以選擇 bfs 即可,從結果出發,每操作一次,儲存一次該...