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:
#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*
#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;
}