一. 题意:
八数码问题,输入一个3*3的棋盘。其中包含一个x和1到8,用x跟上下左右交换。求到满足状态12345678x时x要怎么移动。
二. 解题方法:
我是用康托展开和逆展开,把棋盘9!种不同的状态映射到0到9!-1个整数,然后用整数代表不同的状态,(暂且理解为一个整数代表一个状态,这好像就是传说中的hash算法吧)比如说123456789是一个状态,987654321是另外一个状态。然后一个状态一个状态搜索。
定义数组visited[]标记不同状态是否被访问过,我直接把x那个位置替换为9,然后从9开始搜索(BFS),定义一个结构体Path,用来存放路径。里面的direction用来表示前一个状态是从那个方向来到当前状态的,pre用来表示当前状态是从前面的哪个状态过来的。当遇见所代表的整数等于0,即所代表的棋盘为123456789,就退出。搜索完还没遇到等于0的情况就是没有解。(透露一个秘密:测试数据根本没有没解这种情况,因为一开始没仔细看题,直接猜样例然后就AC了哈哈哈哈)
三. 预备知识:
1. 组合数学的知识:
康托展开:对于一个全排列,将每一个排列又小到大排序,他们的排列顺序代表它们。给出它的排列,求出它第几大。举个例子比较好理解:
比如对于3个数123,它们的排列有123,132,213,231,312,321共6种,用0,1,2,3,4,5分别代表它们。给出一个312,让你求出4。怎么求呢。有点高中数学知识就够了比3小的数有2个。第一位可以是1, 2。第二位为2!然后看1,比1小的有0个。然后如果后面有继续加,只看后面的子序列,不要往回找。
康托逆展开:给数字求排列。从最高位开始,取整得出有几个数比它当前位数小,取余然后再取整,直接的逆运算,仔细想想就明白了。
四. 本来想写一篇长长的,可以解释得比较清楚的,毕竟这题想了好久,也学了好多啊感觉,然后好像也说不出什么了。毕竟我只用了单向BFS,就完了。只能说,详细看代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int MAXSIZE = 362880;
//The list of factorial:
int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
//Mark whether this state have been tried:
bool visited[MAXSIZE];
//The direction of moving:
int movei[] = {-1, 1, 0, 0};
int movej[] = {0, 0, -1, 1};
char direction[] = {'u', 'd', 'l', 'r'};
struct Path
{
//how to move to current state(By up, down, left or right):
char direction;
//Remember how to reach current(from one state to another state):
int pre;
} path[MAXSIZE];
//Every state:
struct state
{
int i, j;
char grid[3][3];
};
//Whether it can be solve:
bool flag;
void order(char str[][3], int &ans)
{
int i, j, k = 0, numOfMin;
char temp[9];
for(i = 0; i < 3; i++)
for(j = 0; j < 3; j++){
temp[k] = str[i][j];
k++;
}
ans = 0;
for(i = 0; i < 8; i++){
numOfMin = 0;
for(j = i + 1; j < 9; j++){
if(temp[i] > temp[j]){
numOfMin++;
}
}
ans += fac[8 - i]*numOfMin;
}
}
void getState(int num, state &ans)
{
int i, j, k = 0, numOfMin;
char temp[9];
for(i = 8; i >= 0; i--){
numOfMin = num/fac[i];
temp[8-i] = '1' + numOfMin;
num = num%fac[i];
}
for(i = 0; i < 3; i++)
for(j = 0; j < 3; j++){
ans.grid[i][j] = temp[k];
k++;
if('9' == ans.grid[i][j]){
ans.i = i;
ans.j = j;
}
}
}
void bfs(state start)
{
queue <state> que;
state now, next;
int i, j, dir, nextOrder, curOrder;
que.push(start);
order(start.grid, curOrder);
path[curOrder].pre = -1;
visited[curOrder] = true;
flag = true;
while(!que.empty()){
now = que.front();
que.pop();
order(now.grid, curOrder);
for(dir = 0; dir < 4; dir++){
i = now.i + movei[dir];
j = now.j + movej[dir];
if(i >= 0 && i < 3 && j >= 0 && j < 3){
next = now;
swap(next.grid[i][j], next.grid[next.i][next.j]);
order(next.grid, nextOrder);
if(!visited[nextOrder]){
visited[nextOrder] = true;
path[nextOrder].direction = direction[dir];
next.i = i, next.j = j;
path[nextOrder].pre = curOrder;
if(0 == nextOrder)
return;
que.push(next);
}
}
}
}
flag = false;
}
void printPath()
{
char way[1024];
int k = 0, curOrder = 0, preOrder;
preOrder = path[0].pre;
for(k = 0; path[curOrder].pre != -1; k++){
preOrder = path[curOrder].pre;
way[k] = path[curOrder].direction;
curOrder = preOrder;
}
for(k--; k >= 0; k--){
cout<<way[k];
}
cout<<endl;
}
int main()
{
//freopen("in.txt", "r", stdin);
int i, j;
state start;
for(i = 0; i < 3; i++)
for(j = 0; j < 3; j++){
cin>>start.grid[i][j];
if('x' == start.grid[i][j]){
start.i = i;
start.j = j;
start.grid[i][j] = '9';
}
}
bfs(start);
if(flag)
printPath();
else
cout<<"unsolvable\n";
}
G++250ms。
回头来看,康托逆展开写错了,仔细看下代码,根本没用过!- -。