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>
usingnamespace std;
constint N =10;
constint 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];//记录当前点地图
booloperator<(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)
{
case0:ppre[next.status] ='d';break;
case1:ppre[next.status] ='u';break;
case2:ppre[next.status] ='r';break;
case3: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)return0;
}
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");
}
}
return0;
}


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

IDA*

View Code
#include<iostream>
#include
<math.h>
#include
<stdio.h>
usingnamespace std;
constint 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";
return0;
}
 
posted @ 2011-03-02 21:45  AndreMouche  阅读(1720)  评论(0编辑  收藏  举报