【逆向BFS + 康托展开 + 打表】hdu 1043 Eight(八数码问题)
题目描述:
http://acm.hdu.edu.cn/showproblem.php?pid=1043
中文大意:
经典八数码问题。
给定初始状态,要求变换到目标状态并输出移动过程。
目标状态固定为:1 2 3 4 5 6 7 8 x 。
思路:
采用逆向 BFS + 康托展开判重 + 打表的方法来做这道题。
八数码问题一共有 9! 种状态,每种状态对应一个 cantor 值。
以目标状态为 BFS 起始状态,搜索这 9! 种状态。
移动信息记录在 paths[362880] 数组中。
paths[i] 中的 i :现状态的 cantor 值,
pahts[i].from :前状态的 cantor 值,
paths[i].dir :前状态->现状态的移动方向。
因为是逆向 BFS,所以输出的时候需要反过来,即上对下,左对右。
队列节点记录的是当前八数码状态和 “x” 的位置。
在弹出队列首节点,确定了当前八数码的状态信息后,下一步有 4 种选择:“x” 上下左右移动。
“康托展开”用来计算当前状态的 cantor 值,判断该状态是否已经被访问 visited[cantor]。
注意:①移动信息不能直接记录在 string 中,否则会超内存限制;
②杭电oj 上的这道题有多组数据,需要 while(cin>>temp)。
代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int state[9];//当前状态
int pos;//"x"的位置
};
int goal[9];
int start[9] = {1,2,3,4,5,6,7,8,0};
//上下左右
int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
//逆向
char dir_str[4] = {'d', 'u', 'r', 'l'};
//Cantor用到的常数
int factory[] = {1,1,2,6,24,120,720,5040,40320,362880};
bool visited[362880] = {false};
struct node2{
int from;//前状态
int dir;//前状态->现状态的移动方向
}paths[362880];
int Cantor(int state[], int n){
int result = 0;
for(int i=0;i<n;i++){
int counted = 0;
for(int j=i+1;j<n;j++){
if(state[i] > state[j]){
counted++;
}
}
result += counted * factory[n-i-1];
}
return result;
}
void bfs(){
node head,next;
memcpy(head.state, start, sizeof(start));
head.pos = 8;
int cantor = Cantor(head.state, 9);
visited[cantor] = true;
paths[cantor].from = -1;
queue<node> q;
q.push(head);
while(!q.empty()){
head = q.front();
q.pop();
int px = head.pos % 3;
int py = head.pos / 3;
int head_cantor = Cantor(head.state, 9);
for(int i=0;i<4;i++){
int nx = px + dir[i][0];
int ny = py + dir[i][1];
if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){
memcpy(next.state, head.state, sizeof(head.state));
next.pos = ny * 3 + nx;
swap(next.state[next.pos], next.state[head.pos]);
cantor = Cantor(next.state, 9);
if(!visited[cantor]){
visited[cantor] = true;
paths[cantor].from = head_cantor;
paths[cantor].dir = i;
q.push(next);
}
}
}
}
}
void print(int cantor){
while (paths[cantor].from != -1){
printf("%c", dir_str[paths[cantor].dir]);
cantor = paths[cantor].from;
}
printf("\n");
}
int main(){
bfs();
char temp;
while (cin>>temp){
goal[0] = temp - '0';
if(goal[0] > 9){
goal[0] = 0;
}
for (int i=1;i<9;i++){
cin>>temp;
goal[i] = temp - '0';
if(goal[i] > 9){
goal[i] = 0;
}
}
int cantor = Cantor(goal, 9);
if (!visited[cantor]){
cout << "unsolvable" << endl;
}
else{
print(cantor);
}
}
}