原题地址: http://acm.hdu.edu.cn/showproblem.php?pid=1043
题意:给一个3*3的方格,每个格子分别有1,2,3,4,5,6,7,8,x这九个编号,其中x这个格子可以通过与其相邻的上下左右格子交换来移动,问x这个格子要经过怎样的移动可以使方格变成1,2,3,4,5,6,7,8,x的状态,如果怎样也不可到达题目要求状态,输出"
unsolvable"。
思路:某物通过不断改变自己的状态,来达到目标状态的题目可以用搜索?可就算知道用搜索可解,这题仍要考虑一下几点:
1.状态的表示:搜索的时候要防止访问已访问过的状态,所以需要对已访问过的状态进行标记,怎样进行标记比较高效?先想到的是吧这个3*3的矩阵看做1*9的数字,但是单开数组是会爆内存的,用map的话,查找也许不是很高效。可以注意到这个1*9的数字其实是9个数的一个排列,因此我们可以用康拓展开来表示当前数是0-8,9个数组成的排列中的第几个排列。用这个编号作为状态的标记。
2.合法排列的判定:如果一个序列本身就不可以到达目标状态,那么搜索是无用的,且会大大增加程序的运行时间。不难发现由于目标状态是没有逆序对的,单单移动x的话,逆序对的出现或者增加都是成对的,同时题目中也有"
In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course). "的提示,自己也不会严格的证明,总之当逆序对的个数为奇数时,是无解的。
3.如何加速搜索:自己第一次接触到A*搜索,百度了一下,这个算法被称作“启发式算法”,该算法通过f值来判断某状态到达目标状态的可能性大小,以此来加速搜索。
其中f=g+h
g为初始状态到达当前状态的步数,这个可以通过计数得到。
h为当前状态估计到达目标状态所需的步数,网上很多人在这题中,用每个点到自己目标位置的曼哈顿距离来估值。
每个状态的f值越小,其找到目标状态的所需花费就越小。想必对h值很好理解,其必然是越小越好。可自己在思考题目的时候,思维一直有个误区,认为g值越大越好,自己想当然地以为一个点离开其实状态越远,找到目标状态的可能性越大,可这是错误的。举个直观的例子,在每个状态的h值为0的情况下,f=g,此时A*搜索退化为广搜,对一般的广搜来说,每次从离起始状态最近的未搜索状态进行搜索,此时搜索的依据就是从g值小的开始。自己网上找了些资料,也都是这样说,但是貌似都没有说清这样做的原因,自己也试了试,的确是g越小越好,自己以后想明白了会补上来的。
自己这个代码写的很混乱,大部分参考的是: http://blog.csdn.net/acm_cxlove/article/details/7745323这里的代码。有需要可以移步这里查看。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define lowbit(x) (x&(-x))
typedef long long LL;
inline void fre1(){freopen("input.txt","r",stdin);/*freopen("output.txt","w",stdout);*/}
inline void fre2(){fclose(stdin);/*fclose(stdout);*/}
const int MAXN=362880+5;
const double EPS=1e-8;
struct Node{
int maxtrix[3][3];
int x,y,g,h,hsh;
bool operator < (const Node& rhs) const{
return g>rhs.g||(g==rhs.g&&h>rhs.h);
}
bool check(){ //??????
if(x>=0&&x<3&&y>=0&&y<3)
return true;
return false;
}
};
int fact[9]={1,1,2,6,14,120,720,5040,40320};
int get_hash(int ma[][3]){
int s[9],k=0;
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
s[k++]=ma[i][j];
// for(int i=0;is[i]) ++cnt;
ret+=cnt*fact[i];
}
return ret;
}
int AIM[][3]={1,2,3,4,5,6,7,8,0};
int aim=get_hash(AIM);
const int dir1[]={0,0,1,-1};
const int dir2[]={1,-1,0,0};
char str[30];
int pre[MAXN],vis[MAXN];
bool myjudge(int ma[][3]){
int s[9],k=0;
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
s[k++]=ma[i][j];
// for(int i=0;is[j]) ++ret;
return !(ret&1);
}
int get_h(int ma[][3]){
int ret=0;
for(int i=0;i<3;++i){
for(int j=0;j<3;++j) if(ma[i][j]){
ret+=(abs(i-(ma[i][j]-1)/3)+abs(j-(ma[i][j]-1)%3));
}
else ret+=4-i-j;
}
return ret;
}
stack S;
void print(){
while(!S.empty()) S.pop();
int nxt=aim;
while(~pre[nxt]){
// cout< Q;
while(!Q.empty()) Q.pop();
Q.push(node);
Node u,v;
bool flag=false;
while(!Q.empty()){
// cout<<"Y"<2||v.y<0||v.y>2) continue;
swap(v.maxtrix[v.x][v.y],v.maxtrix[u.x][u.y]);
v.hsh=get_hash(v.maxtrix);
// cout<='1'&&str[k]<='8')||str[k]=='x'){
int t=node.maxtrix[i][j]=str[k]-'0';
if(t<1||t>8){
node.maxtrix[i][j]=0;
node.x=i,node.y=j;
}
++j;
if(j==3) ++i,j=0;
}
}
// for(int i=0;i<3;++i){
// for(int j=0;j<3;++j) printf("%d",node.maxtrix[i][j]);
// putchar(10);
// }
if(myjudge(node.maxtrix)) bfs(node);
else puts("unsolvable");
}
return 0;
}