【BFS + 康托展开 + 打表】hdu 3567 Eight II(八数码问题)
题目描述:
http://acm.hdu.edu.cn/showproblem.php?pid=3567
中文大意:
给定八数码的初始状态和目标状态,要求输出移动过程。
最终输出的移动过程需要是所有解决方案中长度最短、字典序最小的。
思路:
采用打表的方式来做这道题。
但八数码问题一共有 9! 种状态,以每种状态为广搜起点并记录到达剩余状态的移动信息,工作量太大。
需要将八数码问题抽象成九种初始状态:
012345678
102345678
120345678
123045678
123405678
123450678
123456078
123456708
123456780
例如,初始状态 120453786 和目标状态 123456780,
对应初始状态 120345678 和目标状态 125348670。
状态 120345678 到状态 125348670 的移动过程我们已经知道,按要求输出即可。
其中,移动信息记录在 paths[9][362880] 数组中。
paths[k][i] 中的 k :9 种初始状态,
paths[k][i] 中的 i :现状态的 cantor 值,
pahts[k][i].from :前状态的 cantor 值,
paths[k][i].dir :前状态->现状态的移动方向。
队列节点记录的是当前八数码状态和 “x” 的位置。
在弹出队列首节点,确定了当前八数码的状态信息后,下一步有 4 种选择:“x” 上下左右移动。
“康托展开”用来计算当前状态的 cantor 值,判断该状态是否已经被访问 visited[cantor]。
注意:移动信息不能直接记录在 string 中,否则会超内存限制;
代码:
#include<bits/stdc++.h>
using namespace std;
struct node{
int state[9];
int pos;
};
int start[9];
int goal[9];
int factory[] = {1,1,2,6,24,120,720,5040,40320,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;
}
bool visited[362880];
int dir[4][2] = {{0,1}, {-1,0}, {1,0}, {0,-1}};
char dirs[4] = {'d', 'l', 'r', 'u'};
struct path{
int from;
char dir;
}paths[9][362880];
void a_start(int k){
node head,next;
memcpy(head.state, start, sizeof(start));
head.pos = k;
memset(visited, false, sizeof(visited));
int h_cantor = Cantor(head.state, 9);
visited[h_cantor] = true;
paths[k][h_cantor].from = -1;
queue<node> q;
q.push(head);
while(!q.empty()){
head = q.front();
q.pop();
h_cantor = Cantor(head.state, 9);
for(int i=0;i<4;i++){
int x = head.pos % 3;
int y = head.pos / 3;
int nx = x + dir[i][0];
int ny = y + 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]);
int n_cantor = Cantor(next.state, 9);
if(!visited[n_cantor]){
visited[n_cantor] = true;
paths[k][n_cantor].from = h_cantor;
paths[k][n_cantor].dir = dirs[i];
q.push(next);
}
}
}
}
}
void new_start(int k){
int t = 1;
for(int i=0;i<9;i++){
if(i == k){
start[i] = 0;
}
else{
start[i] = t;
t++;
}
}
}
int main(){
for(int i=0;i<9;i++){
new_start(i);
a_start(i);
}
int num;
scanf("%d", &num);
for(int i=1;i<=num;i++){
char str[9];
scanf("%s", str);
int maps[9];
int k,t = 1;
for(int j=0;j<9;j++){
int num = str[j] - '0';
if(num > 9){
k = j;
num = 0;
maps[num] = 0;
}
else{
maps[num] = t;
t++;
}
}
scanf("%s", str);
for(int j=0;j<9;j++){
int num = str[j] - '0';
if(num > 9){
num = 0;
}
goal[j] = maps[num];
}
string result;
int cantor = Cantor(goal, 9);
while(paths[k][cantor].from != -1){
result = paths[k][cantor].dir + result;
cantor = paths[k][cantor].from;
}
printf("Case %d: %d\n", i, result.length());
cout<<result<<endl;
}
}