Eight
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 28349 Accepted Submission(s): 7540
Special Judge
Problem Description
The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as:
where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively.
Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. 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).
In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 x
where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8 9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12 13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x r-> d-> r->
The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively.
Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. 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).
In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.
Input
You will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle
1 2 3
x 4 6
7 5 8
is described by this list:
1 2 3 x 4 6 7 5 8
1 2 3
x 4 6
7 5 8
is described by this list:
1 2 3 x 4 6 7 5 8
Output
You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases.
Sample Input
2 3 4 1 5 x 7 6 8
Sample Output
ullddrurdllurdruldr
非常牛的题,我断断续续做了两天,中间头都快大了,最后学了不少东西,比如A* 和康托展开在BFS搜索上的应用。以后有时间还要继续做。建议做此题前请提前留出来起码半天的时间。
第一种:双向BFS
#include<cstdio>
#include<queue>
#include<string>
#include<string.h>
using namespace std;
//一个迷宫,假设其是没有边界,没有墙的空地,单BFS需要4^n时间,那双向BFS就是4^(n/2)*2,缩短了特别多。
const int maxn=5e5+10;
int hs[9]={1,1,2,6,24,120,720,5040,40320};
struct node{
int num;
char ch;
}pre[maxn];
struct node2{
string c;
int num;
}e;
int vis[maxn];
int vis2[maxn];
int dir[4]={-3,3,-1,1};
char a[30],d[5]="udlr",d2[5]="durl";
string b="123456780";
void show(int x)//输出
{
if(pre[x].num==-1)
return;
show(pre[x].num);
printf("%c",pre[x].ch);
}
int get_hash(string e)//获得该状态的康拓展开哈希值
{
int s=0,i,j,k;
for(i=0;i<9;i++)
{
k=0;
for(j=0;j<i;j++)
if(e[j]>e[i])
k++;
s+=k*hs[i];
}
return s;
}
void bfs() //同时从起点状态和终点状态开始遍历,使其在中间相遇
{
vis[get_hash(e.c)]=1;
pre[1].num=-1;
int num=2,x,i,j,k,p,q;
node2 f,g;
f.c=b;
f.num=8;
pre[2].num=-1;
vis2[get_hash(f.c)]=2;
queue<node2>q1;
queue<node2>q2;
q1.push(e);
q2.push(f);
while(!q1.empty()&&!q2.empty())
{
f=q1.front();
q1.pop();
p=get_hash(f.c);
if(vis2[p])
{
show(vis[p]);
k=vis2[p];
while(pre[k].num!=-1)
{
printf("%c",pre[k].ch);
k=pre[k].num;
}
printf("\n");
return;
}
for(i=0;i<4;i++)
{
if(i==0&&f.num<3) continue;
if(i==1&&f.num>5) continue;
if(i==2&&f.num%3==0)continue;
if(i==3&&f.num%3==2)continue;
x=f.num+dir[i];
g=f;
swap(g.c[f.num],g.c[x]);
q=get_hash(g.c);
if(vis[q])
continue;
num++;
vis[q]=num;
g.num=x;
pre[num].num=vis[p];
pre[num].ch=d[i];
q1.push(g);
}
f=q2.front();
q2.pop();
p=get_hash(f.c);
//cout<<f.c<<endl;
if(vis[p])
{
show(vis[p]);
k=vis2[p];
while(pre[k].num!=-1)
{
printf("%c",pre[k].ch);
k=pre[k].num;
}
printf("\n");
return ;
}
for(i=0;i<4;i++)
{
if(i==0&&f.num<3)continue;
if(i==1&&f.num>5)continue;
if(i==2&&f.num%3==0)continue;
if(i==3&&f.num%3==2)continue;
x=f.num+dir[i];
g=f;
swap(g.c[f.num],g.c[x]);
q=get_hash(g.c);
if(vis2[q])continue;
vis2[q]=++num;
g.num=x;
pre[num].num=vis2[p];
pre[num].ch=d2[i];
q2.push(g);
}
}
printf("unsolvable\n");
}
int main()
{
while(gets(a))
{
int i,j,k=0,n;
n=strlen(a);
e.c="";
for(i=0,j=0;i<n;i++)
{
if(a[i]!=' ')
{
if(a[i]=='x')
{
e.num=j;
e.c+='0';
}
else
e.c+=a[i];
j++;
}
}
for(i=0;i<9;i++)
{
if(e.c[i]=='0')
continue;
for(j=0;j<i;j++)
{
if(e.c[j]=='0')
continue;
if(e.c[j]>e.c[i])
k++;
}
}
memset(vis2,0,sizeof(vis2));
memset(vis,0,sizeof(vis));
if(k&1)
printf("unsolvable\n");
else
bfs();
}
return 0;
}
第二种:A*
#include<stdio.h>
#include<queue>
#include<math.h>
#include<string.h>
using namespace std;
const int maxn=4e5+10;
int ha[9]={1,1,2,6,24,120,720,5040,40320};
int dir[4][2]={-1,0,1,0,0,-1,0,1};
char d[5]="udlr";
int vis[maxn];
int i,j,k;
struct node{
int f[3][3];
int x,y;
int g,h;
int hash_num;
bool operator < (const node a)const{
//return h==a.h? g>a.g : h>a.h;
return h+g>a.h+a.g;
}
};
struct path{
int pre;
char ch;
}p[maxn];
int get_hash(node e) //康托展开,压缩空间
{
int a[9],ans=0;
k=0;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
a[k++]=e.f[i][j];
}
for(i=0;i<9;i++)
{
k=0;
for(j=0;j<i;j++)
if(a[j]>a[i])
k++;
ans+=ha[i]*k;
}
return ans;
}
//评估函数,获得评估值
//计算1~8的数字回到原点需要的步数作为评估值,一定是小于实际操作数的
int get_h(node e)
{
int ans=0;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
if(e.f[i][j])
ans+=abs(i-(e.f[i][j]-1)/3)+abs(j-(e.f[i][j]-1)%3);
}
}
return ans;
}
void print(int x)
{
if(p[x].pre==-1)
return;
print(p[x].pre);
printf("%c",p[x].ch);
}
void A_star_BFS(node e)
{
int end_ans,xx,yy;
node a;
for(i=0;i<9;i++)
a.f[i/3][i%3]=(i+1)%9;
end_ans=get_hash(a);//记录目标状态的康拓展开哈希值
e.hash_num=get_hash(e);
e.g=0;e.h=get_h(e);
vis[e.hash_num]=1;
p[e.hash_num].pre=-1;
if(e.hash_num==end_ans)
{
printf("\n");
return;
}
priority_queue<node> q;
q.push(e);
while(!q.empty())
{
e=q.top();
q.pop();
for(i=0;i<4;i++)
{
xx=e.x+dir[i][0];
yy=e.y+dir[i][1];
if(xx<0||yy<0||xx>=3||yy>=3)
continue;
a=e;
swap(a.f[e.x][e.y], a.f[xx][yy]);
k=get_hash(a);
if(vis[k])
continue;
vis[k]=1;
a.hash_num=k;
a.x=xx;
a.y=yy;
a.g++;
a.h=get_h(a);
p[k].pre=e.hash_num;
p[k].ch=d[i];
if(k==end_ans)
{
print(k);
printf("\n");
return;
}
q.push(a);
}
}
}
int main()
{
char a[30];
while(gets(a))
{
int n;
node e;
n=strlen(a);
for(i=0,j=0;i<n;i++) //数据处理
{
if(a[i]==' ')continue;
if(a[i]=='x')
{
e.f[j/3][j%3]=0;
e.x=j/3;
e.y=j%3;
}
else
e.f[j/3][j%3]=a[i]-'0';
j++;
}
//求出此状态逆序数
for(i=0,k=0;i<9;i++)
{
if(e.f[i/3][i%3]==0)
continue;
for(j=0;j<i;j++)
{
if(e.f[j/3][j%3]==0)
continue;
if(e.f[j/3][j%3]>e.f[i/3][i%3])
k++;
}
}
memset(vis,0,sizeof(vis));
if(k&1)
printf("unsolvable\n");
else
A_star_BFS(e);
}
return 0;
}