从头到尾都透露出八数码的气息的一个题。。。果断写A*了。
没做过八数码问题或A*搜索的强烈建议先做这道题:Eight
相关资料跟题解网上一大堆。
这道题跟八数码的不同在于,可以从一个边界跑到另一个边界,按照图中的字母对应。并且这题给出了横向移动和纵向移动各自的消耗,并且确保一个状态能到达另一个状态,求始态到终态的最低消耗。
我是利用弗罗伊德算法预处理出每个位置到其他位置的最低消耗,用于估价函数的计算。
当从队列里面取出的元素是目标状态时停止搜索。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int vis[1000000];
char str[10], ss[10];
int fac[10], target[10];
bool has[10];
int dis[10][10];
int hg[10][2], vg[10][2];//从某个位置出发可以到达的位置
int hi[10][2], vi[10][2];//可以到达该位置的位置
int find(char s[]){//康托展开
int res = 0;
memset(has, 0, sizeof(has));
for(int i=0; i<9; i++){
int x = s[i]-'0', y=0;
for(int j=1; j<x; j++){
if(!has[j]) y++;
}
res += y*fac[8-i];
has[x] = 1;
}
return res;
}
int get_h(const char s[]){//估价函数
int res = 0;
for(int i=0; i<9; i++){
res += dis[i][target[s[i]-'0']];
}
return res;
}
struct Node{
char s[10];
int g;
Node(){}
Node(char s[], int g):g(g){
strcpy(this->s, s);
}
bool operator < (const Node& o)const{
return g+get_h(s) > o.g+get_h(o.s);
}
};
void init(){
fac[0] = 1;
for(int i=1; i<9; i++) fac[i] = fac[i-1]*i;
for(int i=0; i<9; i++){//求出i位置出发可以到达的位置,用于计算移动消耗
hg[i][0] = (i+1)%9;
hg[i][1] = (i+8)%9;
vg[i][0] = (i+3)%9;
vg[i][1] = (i+6)%9;
}
memset(vi, -1, sizeof(vi));
memset(hi, -1, sizeof(hi));
for(int i=0; i<9; i++){
for(int j=0; j<2; j++){
int x = vg[i][j];//反过来记录可以到达x的位置,用于搜索中移动的计算
if(vi[x][0]==-1) vi[x][0] = i;
else vi[x][1] = i;
x = hg[i][j];
if(hi[x][0]==-1) hi[x][0] = i;
else hi[x][1] = i;
}
}
}
int A, B;
void cal(){
for(int i=0; i<9; i++){
for(int j=0; j<9; j++) dis[i][j] = 1000000000;
for(int j=0; j<2; j++){
dis[i][vg[i][j]] = B;
dis[i][hg[i][j]] = A;
}
dis[i][i] = 0;
}
//弗罗伊德算法
for(int k=0; k<9; k++){
for(int i=0; i<9; i++){
for(int j=0; j<9; j++)
dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
}
}
}
int main(){
init();
int T, d, x;
while(~scanf("%d %d", &A, &B) && (A||B)){
for(int i=0; i<9; i++){
scanf("%d", &d);
if(d==0) d = 9;
str[i] = d+'0';
}
str[9] = '\0';
for(int i=0; i<9; i++){
scanf("%d", &d);
if(d==0) d = 9;
target[d] = i;
ss[i] = d+'0';
}
T = find(ss);
if(T==find(str)){
puts("0");
continue;
}
cal();
memset(vis, -1, sizeof(vis));
priority_queue<Node> Q;
Q.push(Node(str, 0));
while(!Q.empty()){
Node nd = Q.top(); Q.pop();
x = find(nd.s);
if(x==T){
printf("%d\n", nd.g);
break;
}
if(~vis[x] && nd.g >= vis[x]) continue;
vis[x] = nd.g;
x = 0;
while(nd.s[x]!='9') x++;
for(int i=0; i<2; i++){
strcpy(ss, nd.s);
ss[x] = ss[vi[x][i]];
ss[vi[x][i]] = '9';
d = find(ss);
if(vis[d]==-1 || nd.g+B<vis[d]){
Q.push(Node(ss, nd.g+B));
}
ss[vi[x][i]] = ss[x];
ss[x] = ss[hi[x][i]];
ss[hi[x][i]] = '9';
d = find(ss);
if(vis[d]==-1 || nd.g+A<vis[d]){
Q.push(Node(ss, nd.g + A));
}
}
}
}
return 0;
}