您的位置:首页 > 其它

POJ1077Eight

2016-03-04 20:11 232 查看
一. 题意:

八数码问题,输入一个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。

回头来看,康托逆展开写错了,仔细看下代码,根本没用过!- -。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: