Eight
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
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 8Sample Output
ullddrurdllurdruldr
题意很简单,给你一个状态让你变成给定状态12345678X,输出路径,如果不行输出unsolvable
网上博客上有很多种做法,什么数码八大境界之类的,大家自己搜去,这里就只写了最简单也最容易理解的一种方法,bfs逆向打表+康拓展开,因为目标状态只有一种,而从目标状态可以变到的状态也是有限种,我们可以从目标状态为起点,进行bfs保存下从目标状态到达每一种状态的路径,这里的状态就是那个数组的不同顺序,因为直接存数组肯定超内存,而这是一个序列的不同排列,因此可以将状态通过康拓展开进行压缩hash一下,这样状态就只用一个数表示就可以了,因为是逆向的存储,所以只需要让方向数组和对应的方向字符数组恰好相反即可使得最终路径是正向的,这里用到了康托展开和康拓逆展开了解一下。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int maxn = 362880;
int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
char Indexx[5] = "durl";//与上面的方向是相反的,因为是逆向搜索的
int num[9];
int t[9];//临时存每个康拓展开值对应的排列
int Hash[maxn];//是否有这种状态
string path[maxn];
struct node{
int pos;//x在一维中的位置
int val;//康拓展开值
string path;//移动路径
};
int Cantor(int *s){
int sum = 0;
for(int i = 0; i < 9; i++){
int num = 0;
for(int j = i+1; j < 9; j++){
if(s[j] < s[i])
num++;//确定当前元素i在未出现的元素中是第几个(从小到大)
}
sum += fac[8-i] * num;
}
return sum;
}
void CantorReverse(int val,int *s){
//val--;
int vis[10] = {0};
for(int i = 0; i < 9; i++){
int tmp = val / fac[8-i];
for(int j = 0; j <= tmp; j++){
if(vis[j]) tmp++;
}
t[i] = tmp + 1;
vis[tmp] = 1;
val %= fac[8-i];
}
return;
}
void Bfs(){
memset(Hash,0,sizeof(Hash));
node st,ed;
for(int i = 0; i < 8; i++){
t[i] = i+1;
}
t[8] = 9;
st.pos = 8;
st.val = Cantor(t);
st.path = "";
queue<node> q;
path[st.val] = "";
q.push(st);
while(!q.empty()){
st = q.front();
q.pop();
int x = st.pos / 3;
int y = st.pos % 3;
for(int i = 0; i < 4; i++){
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if(xx >= 0 && xx < 3 && yy >= 0 && yy < 3){
ed = st;
ed.pos = xx * 3 + yy;
CantorReverse(st.val,t);
swap(t[ed.pos],t[st.pos]);//交换
ed.val = Cantor(t);
if(!Hash[ed.val]){
Hash[ed.val] = 1;
ed.path = Indexx[i] + ed.path;//从上一路径基础上移动
q.push(ed);
path[ed.val] = ed.path;
}
}
}
}
}
int main(){
char str[110];
Bfs();
while(gets(str)){
int len = strlen(str);
int cnt = 0;
for(int i = 0; i < len; i++){
if(str[i] >= '0' && str[i] <= '9')
num[cnt++] = str[i] - '0';
else if(str[i] == 'x')
num[cnt++] = 9;
}
if(Hash[Cantor(num)])
cout << path[Cantor(num)] << endl;
else cout << "unsolvable" << endl;
}
return 0;
}
今天学习了一下A*算法
下面有一篇博客写的非常清楚简单明白A*算法
这道题A*算法可以以较快速度求出答案路径,但是根据估值函数的不同却不一定能保证最优,但是能保证最快
下面是这道题A*算法的写法
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<map>
#include<string>
#define inf 1<<30
#define eps 1e-7
#define LD long double
#define LL long long
#define maxn 1000000005
using namespace std;
const int ed=322560;
int Hash[9]= {1,1,2,6,24,120,720,5040,40320};
struct node
{
int Map[3][3];
int h,g;
int x,y;
int Hash;
bool operator<(const node n1)const //优先队列第一关键字为h,第二关键字为g
{
return h!=n1.h?h>n1.h:g>n1.g;
}
bool cheak()
{
if(x>=0&&x<3&&y>=0&&y<3)
return true ;
return false ;
}
} u,v;
int vis[400000];
int pri[400000];
int dir[4][2]= {0,1,0,-1,1,0,-1,0};
bool ok(node tmp) //判断状态是否合法 逆序数为偶数
{
int a[9],k=0;
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
a[k++]=tmp.Map[i][j];
int ans=0;
for(int i=0; i<k; i++)
for(int j=i+1; j<k; j++)
if(a[i]&&a[j]&&a[i]>a[j])
ans++;
return (ans&1);
}
int get_hash(node tmp) //获得康拓值
{
int ans=0;
int a[9],k=0;
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
a[k++]=tmp.Map[i][j];
for(int i=0; i<k; i++)
{
int sum=0;
for(int j=0; j<i; j++)
if(a[j]>a[i])
sum++;
ans+=Hash[i]*sum;
}
return ans;
}
int get_h(node tmp) //估价函数 每一个点移动到原来地方的最短路程和 曼哈顿距离
{
int ans;
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
if(tmp.Map[i][j])
ans+=abs(i-(tmp.Map[i][j]-1)/3)+abs(j-(tmp.Map[i][j]-1)%3);
return ans;
}
void print()
{
string str;
str.clear();
int st=ed;
while(pri[st]!=-1)
{
if(vis[st]==0)
{
str+='r';
}
else if(vis[st]==1)
{
str+='l';
}
else if(vis[st]==2)
{
str+='d';
}
else
str+='u';
st=pri[st];
}
for(int i=str.size()-1;i>=0;i--)
cout<<str[i];
cout<<endl;
return ;
}
void bfs()
{
priority_queue<node >q;
q.push(u);
memset(vis,-1,sizeof(vis));
memset(pri,-1,sizeof(pri));
vis[u.Hash]=-2;
while(!q.empty())
{
//cout<<q.size()<<endl;
u=q.top();
q.pop();
for(int i=0; i<4; i++)
{
v=u;
v.x+=dir[i][0];
v.y+=dir[i][1];
if(v.cheak())
{
swap(v.Map[v.x][v.y],v.Map[u.x][u.y]);
v.Hash=get_hash(v);
if(vis[v.Hash]==-1&&!ok(v))
{
v.h=get_h(v);
v.g++;
vis[v.Hash]=i;
pri[v.Hash]=u.Hash;
q.push(v);
}
}
if(v.Hash==ed)
{
print();
return ;
}
}
}
}
int main()
{
char ch[100];
memset(ch,0,sizeof(ch));
while(cin>>ch[0])
{
for(int i=1; i<9; i++)
cin>>ch[i];
for(int i=0; i<3; i++)
{
for(int j=0; j<3; j++)
{
if(ch[i*3+j]=='x')
{
u.Map[i][j]=0;
u.x=i;
u.y=j;
u.Hash=get_hash(u);
u.h=get_h(u);
u.g=0;
}
else u.Map[i][j]=ch[i*3+j]-'0';
}
}
if(u.Hash==ed)
{
cout<<endl;
continue ;
}
if(!ok(u))
bfs();
else cout<<"unsolvable"<<endl;
}
}