POJ 1077 Eight【八数码问题】

http://poj.org/problem?id=1077
八数码问题:
由1,2,3,4,5,6,7,8,x组成一个3*3的矩阵,如
1 2 3
4 x 5  
6 7 8
其中x可与其上,下,左,右相邻的元素互换,
现问从给出状态出发到达以下状态:
1 2 3
4 5 6
7 8 x
需要对x进行怎样的位移操作,输出x的最少位移信息,
若状态不可达,输出unsolvable

分析:
1.一个很容易的想法,即BFS,
2.用康托展开将每个状态转化为整数,即可略过已访问点
3.再用优先队列加以优化
4.其实可以用A*加以优化,f(x) = g(x)+h(x),h(x)为当前状态中x所在位置到右下角位置的哈密尔顿距离(不过实际中没遇到)
5.如果是单case的话,用以上即可水过,
  但若是多case,复杂度会过高,超时。
6.解决多case的方法是,在预处理中从目标状态出发BFS,记录所有可达状态信息。
  这样,对于输入的任何一种状态,便可直接输出答案(又邪恶了吧。。)

PS:
问题在BFS过程略过已搜索过的状态
纠结了很久之后,终于还是不忍去网上搜了一下,
呵呵,居然让我发现还有康托展开这么个好东东!
关于康托展开,百度百科上说得还是比较清楚的:http://baike.baidu.com/view/437641.htm
简单说一下:
  将x抽象为数字9,对于每个状态,可映射为{1,2,3,4,5,6,7,8,9}的一个排列。
  根据康托展开,即将具体的排列转化为一个数字,即可保存其状态

这个该死的康托,还是用了一定的时间去搞的,可惜,我只知道怎样把一个排列变成数字,把数字变成排列是怎么也搞不来,
所以,偷偷小懒,干脆把数组扔进了节点里,邪恶了。。。

因为POJ上是单case的,所以果断从给出的状态BFS直到搜到最终状态为止,就这样邪恶的水过。。
ZOJ和HDU貌似很是不给面子,多case,果断超时。。。。
回来继续研究,赫,不让我怎么搞,我就倒着搜呗,于是想到了在预处理中从目标点BFS一次,记录下所有可达状态,O(∩_∩)O哈,就这么水水地又过了。。

Code BFS:

View Code
#include < stdio.h >
#include
< string .h >
#include
< queue >
using namespace std;
const int N = 10 ;
const int Upper = 362880 + 1 ;
bool visited[Upper];
int pre[Upper];
char ppre[Upper];
int mod[ 9 ] = { 40320 , 5040 , 720 , 120 , 24 , 6 , 2 , 1 , 1 };
int dxy[ 4 ][ 2 ] = {{ - 1 , 0 },{ 1 , 0 },{ 0 , - 1 },{ 0 , 1 }};
char ansstr[Upper];
struct node
{
int status;
int step;
int array[ 9 ]; // 记录当前点地图
bool operator < ( const node & A) const
{
return step > A.step;
}
};
int a[N];

int getStatus()
{
int ans = 0 ;
int i,j;
for (i = 0 ;i <= 8 ;i ++ )
{
int tn = 0 ;
for (j = i + 1 ;j < 9 ;j ++ )
if (a[j] < a[i])tn ++ ;
ans
= ans + tn * mod[i];
}
return ans;
}
void outputStatus()
{
int i;
for (i = 0 ;i < 9 ;i ++ )
printf(
" %d, " ,a[i]);
printf(
" \n " );
}

/*
从目标状态即(1,2,3,4,5,6,7,8,x)出发,BFS,标记所有可达点
*/
void BFS()
{
memset(visited,
false , sizeof (visited));
node cur;
int i;
cur.step
= 0 ;
i
= 0 ;
for (i = 0 ;i < 9 ;i ++ )
a[i]
= i + 1 ;
cur.status
= getStatus();
for (i = 0 ;i < 9 ;i ++ )
cur.array[i]
= a[i];

pre[cur.status]
=- 1 ;
priority_queue
< node > Q;
Q.push(cur);
visited[cur.status]
= true ;
node next;
while ( ! Q.empty())
{
cur
= Q.top();
Q.pop();
int nine = 0 ;
int ta[ 10 ];
for (i = 0 ;i < 9 ;i ++ )
{
a[i]
= cur.array[i];
ta[i]
= a[i];
if (ta[i] == 9 )nine = i;
}
int ni = nine / 3 ;
int nj = nine % 3 ;
for (i = 0 ;i < 4 ;i ++ )
{
int ti = ni + dxy[i][ 0 ];
int tj = nj + dxy[i][ 1 ];
if (ti >= 0 && ti < 3 && tj >= 0 && tj < 3 )
{
int tp = ti * 3 + tj;
a[tp]
= ta[nine];
a[nine]
= ta[tp];
next.status
= getStatus();
next.step
= cur.step + 1 ;
if (visited[next.status] == false )
{
int kk ;
for (kk = 0 ;kk < 9 ;kk ++ )
next.array[kk]
= a[kk];
Q.push(next);
visited[next.status]
= true ;
pre[next.status]
= cur.status;
switch (i)
{
case 0 :ppre[next.status] = ' d ' ; break ;
case 1 :ppre[next.status] = ' u ' ; break ;
case 2 :ppre[next.status] = ' r ' ; break ;
case 3 :ppre[next.status] = ' l ' ; break ;
}
}
a[tp]
= ta[tp];
a[nine]
= ta[nine];
}

}
}

}

int main()
{
BFS();
while ( true )
{
int i;
for (i = 0 ;i < 9 ;i ++ )
{
char c = ' ' ;
while (c != ' x ' && (c <= ' 0 ' || c > ' 9 ' ))
{
if ((c = getchar()) == EOF) return 0 ;
}
if (c == ' x ' )
a[i]
= 9 ;
else
a[i]
= c - ' 0 ' ;
}
int status = getStatus();
if (visited[status] == false )
printf(
" unsolvable\n " );
else
{
int kk = 0 ;
i
= status;
while (pre[i] !=- 1 )
{
printf(
" %c " ,ppre[i]);
i
= pre[i];
}
printf(
" \n " );
}
}
return 0 ;
}


IDA*
很巧妙的方法,从人家http://www.cnblogs.com/liyongmou/archive/2010/07/19/1780861.html那里偷来的,个人觉得很不错,也在这里贴一下,(*^__^*) 嘻嘻……

IDA*

View Code
#include < iostream >
#include
< math.h >
#include
< stdio.h >
using namespace std;
const int SIZE = 3 ;
char board[SIZE][SIZE];
// 启发函数,除去x之外到目标网格的距离和
int goal_state[ 9 ][ 2 ] = {{ 0 , 0 },{ 0 , 1 },{ 0 , 2 },{ 1 , 0 },{ 1 , 1 },{ 1 , 2 },{ 2 , 0 },{ 2 , 1 },{ 2 , 2 }};
int step[ 4 ][ 2 ] = {{ - 1 , 0 },{ 0 , - 1 },{ 0 , 1 },{ 1 , 0 }}; // ulrd
char op[ 5 ] = " ulrd " ;

int h( char board[][SIZE])
{
int cost = 0 ;
int i,j;
for (i = 0 ;i < SIZE;i ++ )
for (j = 0 ;j < SIZE;j ++ )
{
if (board[i][j] != SIZE * SIZE)
cost
+= abs(i - goal_state[board[i][j] - 1 ][ 0 ]) +
abs(j
- goal_state[board[i][j] - 1 ][ 1 ]);
}
return cost;
}
inline
int min( int a, int b)
{
return a < b ? a:b;
}
char solution[ 1000 ];
int bound; // 上界
bool ans; // 是否找到答案
// DFS返回next_bound
int DFS( int x, int y, int dv, char pre_move)
{
int hv = h(board);
if (hv + dv > bound)
return hv + dv;
if (hv == 0 )
{
ans
= true ;
return dv;
}

int next_bound = 1e9;
int i;
for (i = 0 ;i < 4 ;i ++ )
{
if (i + pre_move == 3 ) // 与上一步相反的移动
continue ;
int nx = x + step[i][ 0 ];
int ny = y + step[i][ 1 ];
if ( 0 <= nx && nx < SIZE && 0 <= ny && ny < SIZE)
{
solution[dv]
= i;
swap(board[x][y],board[nx][ny]);
int new_bound = DFS(nx,ny,dv + 1 ,i);
if (ans)
return new_bound;
next_bound
= min(next_bound,new_bound);
swap(board[x][y],board[nx][ny]);
}
}
return next_bound;
}

void IDA_star( int sx, int sy)
{
ans
= false ;
bound
= h(board); // 初始化代价
while ( ! ans && bound <= 100 ) // 上限
{
bound
= DFS(sx,sy, 0 , - 10 );
}
}
int main()
{
int sx,sy;
char c;
int i,j;
for (i = 0 ;i < SIZE;i ++ )
for (j = 0 ;j < SIZE;j ++ )
{
cin
>> c;
if (c == ' x ' )
{
board[i][j]
= SIZE * SIZE;
sx
= i;
sy
= j;
}
else
board[i][j]
= c - ' 0 ' ;
}

IDA_star(sx,sy);

if (ans)
{
for (i = 0 ;i < bound;i ++ )

cout
<< op[solution[i]];

}
else
cout
<< " unsolvable " ;
return 0 ;
}
 

你可能感兴趣的:(poj)