Eight(经典题,八数码)
Eight
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 20993 Accepted Submission(s): 5634
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:
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
Sample Output
//就是类似九宫格那个游戏,不过这里9个格子大小都相等
//学了比较多的东西,才懂怎么做
这里我用的的是 A*+逆序数剪枝+hash判重 做的
A*其实也好理解,就是 bfs 升级版
这个博客写的很详细 http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx
因为,无论怎么移动,逆序数的奇偶性是不变的,所以用这个能剪枝
最大的问题就是怎么判重了,最多不过 9!种情况么,362880 ,每种情况对应一个数字,用一个 vis[ ]就能判重了,然后hash 判重就好理解了
还有许多方法,推荐一篇博客 http://www.cnblogs.com/goodness/archive/2010/05/04/1727141.html 有兴趣可以看看
A* + hash 判重 + 曼哈顿距离
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <vector>
using namespace std; const int maxn=4e5+;// 400010 最多不超过这么多状态 362880
const int hash_[]={,,,,,,,,};
struct Node
{
int state[][];
int x,y;
int g,h; //g代表已耗费,h代表估计耗费
int hash_num; //这个状态的 hash 值
bool operator < (const Node PP) const
{
//return g+h > PP.g+PP.h;
//1638ms
return h==PP.h ? g>PP.g : h>PP.h;
//686ms
}
}star,ans; int dx[]={,-,,};
int dy[]={,,,-};
char op_c[]={"durl"};
struct Node2
{
int pre_hash;
char op;
}p[maxn];
int vis[maxn]; int count_hash(Node p)//获得hash值,计算的是 0-8 排列的hash
{
int i,j,k,hash_num=;
for (i=;i<;i++)
{
k=;
for (j=;j<i;j++)
{
if (p.state[j/][j%]>p.state[i/][i%])
k++;
}
hash_num+=k*hash_[i];
}
return hash_num;
} int count_h(Node p)//注意位置,要细致
{
int i,all=;
for (i=;i<;i++)
{
int e=p.state[i/][i%];
if (e)
{
e-=;
all+=abs(i/-e/)+abs(i%-e%);
}
}
return all;
} void print(int h)
{
if (p[h].pre_hash==-) return;
print(p[h].pre_hash);
printf("%c",p[h].op);
} void A_star()
{
int i; star.hash_num = count_hash(star);
star.g=;
star.h=count_h(star); memset(vis,,sizeof(vis));
vis[star.hash_num]=;
p[star.hash_num].pre_hash=-; //头节点 priority_queue <Node> Q;
Q.push(star); Node e,n;
int xx,yy; if (star.hash_num==ans.hash_num)//这个不能丢
{
printf("\n");
return;
}
while (!Q.empty())
{
e=Q.top();
Q.pop(); for (i=;i<;i++)
{
xx=e.x+dx[i];
yy=e.y+dy[i];
if (xx<||yy<||xx>=||yy>=) continue; n=e;
n.x=xx;
n.y=yy;
swap(n.state[xx][yy],n.state[e.x][e.y]);
n.g++;
n.h=count_h(n);
n.hash_num=count_hash(n); if (vis[n.hash_num]) continue; p[n.hash_num].pre_hash=e.hash_num; //记录这个状态的的父 hash
p[n.hash_num].op=op_c[i]; //记录怎么由父hash移动来的 vis[n.hash_num]=;
if (n.hash_num==ans.hash_num)//说明到了
{
print(n.hash_num);
printf("\n");
return;
}
Q.push(n);
}
}
} int main()
{
char str[];
int i; for(i=;i<;i++) //终点
ans.state[i/][i%]=(i+)%;
ans.hash_num=count_hash(ans); //终点 while(gets(str))
{
int i;
int len=strlen(str); int j=;
for(i=,j=;i<len;i++)
{
if(str[i]==' ')continue;
if(str[i]=='x')
{
star.state[j/][j%]=;
star.x=j/;
star.y=j%;
}
else star.state[j/][j%]=str[i]-'';
j++; // j/3 记录行数
} //判断逆序数
int temp [],k=;
for (i=;i<;i++)
temp[i]=star.state[i/][i%];
for (i=;i<;i++)
{
if (temp[i]==) continue;
for (int j=;j<i;j++)
if (temp[j]>temp[i]) k++;
}
if (k%)
printf("unsolvable\n");
else
A_star();
}
return ;
} /*//错在这,浪费我2小时,才找出来! == 竟然不报错,而且是全局变量,第一次也不会错!!! for (i=0,j=0;i<len;i++)
{
if (str[i]==' ')continue;
else if (str[i]=='x')
{
star.x=j/3;
star.y=j%3;
star.state[j/3][j%3]==0;
}
else star.state[j/3][j%3]=str[i]-'0';
j++;
}
*/
IDA* + 曼哈顿距离 这个空间就省下来了,改一下,十五数码也能做的,上一个就不行了,内存爆炸
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h> #define size 3 //这里规定几数码 int move[][]={{-,},{,-},{,},{,}};//上 左 右 下 置换顺序
char op[]={'u','l','r','d'}; int map[size][size],map2[size*size],limit,path[];
int flag,length; // 十五数码 的表
//int goal[16][2]= {{size-1,size-1},{0,0},{0,1},{0,2},
// {0,3},{1,0},{1,1},{1,2},
// {1,3},{2,0},{2,1},{2,2},
// {2,3},{3,0},{3,1},{3,2}};//各个数字应在位置对照表 // 八数码 的表
int goal[][]= {{size-,size-},{,},{,},{,},
{,},{,},{,},
{,},{,}}; //各个数字应在位置对照表 int nixu(int a[size*size])
{
int i,j,ni,w,x,y; //w代表0的位置 下标,x y 代表0的数组坐标
ni=;
for(i=;i<size*size;i++) //,size*size=16
{
if(a[i]==) //找到0的位置
continue;//w=i; for(j=i+;j<size*size;j++) //注意!!每一个都跟其后所有的比一圈 查找小于i的个数相加
{
if (a[j]==)continue;
if(a[i]>a[j])
ni++;
}
}
//x=w/size;
//y=w%size;
//ni+=abs(x-(size-1))+abs(y-(size-1)); //最后加上0的偏移量
if(ni%==)
return ;
else
return ;
} int hv(int a[][size])//估价函数,曼哈顿距离,小等于实际总步数
{
int i,j,cost=;
for(i=;i<size;i++)
{
for(j=;j<size;j++)
{
int w=map[i][j];
cost+=abs(i-goal[w][])+abs(j-goal[w][]);
}
}
return cost;
} void swap(int*a,int*b)
{
int tmp;
tmp=*a;
*a=*b;
*b=tmp;
} void dfs(int sx,int sy,int len,int pre_move)//sx,sy是空格的位置
{
int i,nx,ny; if(flag)
return; int dv=hv(map); if (len==limit)
{
if(dv==) //成功! 退出
{
flag=;
length=len;
return;
}
else
return; //超过预设长度 回退
}
else if(len<limit)
{
if(dv==) //短于预设长度 成功!退出
{
flag=;
length=len;
return;
}
} for(i=;i<;i++)
{
if(i+pre_move== && len>)//不和上一次移动方向相反,对第二步以后而言
continue;
nx=sx+move[i][]; //移动的四步 上左右下
ny=sy+move[i][]; if( <=nx && nx<size && <=ny && ny<size ) //判断移动合理
{
swap(&map[sx][sy],&map[nx][ny]);
int p=hv(map); //移动后的 曼哈顿距离p=16 if(p+len<=limit&&!flag) //p+len<=limit&&!flag剪枝判断语句
{
path[len]=i;
dfs(nx,ny,len+,i); //如当前步成功则 递归调用dfs
if(flag)
return;
}
swap(&map[sx][sy],&map[nx][ny]); //不合理则回退一步
}
}
} int main()
{
int pp;
int i,j,k,sx,sy;
char str[]; while(gets(str))
{
int slen = strlen(str);
j=;
for(i=;i<slen;i++)
{
if (str[i]==' ')
continue;
if (str[i]=='x')
map2[j]=;
else
map2[j]=str[i]-'';
j++;
} for(i=;i<size*size;i++) //给map 和map2赋值map是二维数组,map2是一维数组
{
if(map2[i]==)
{
map[i/size][i%size]=;
sx=i/size;
sy=i%size;
}
else
{
map[i/size][i%size]=map2[i];
}
}
flag=,length=;
memset(path,-,sizeof(path)); //已定义path[100]数组,将path填满-1
if(nixu(map2)==) //该状态可达
{
limit=hv(map); //全部的曼哈顿距离之和
while(!flag&&length<=)//要求50步之内到达
{
dfs(sx,sy,,);
if(!flag)
limit++; //得到的是最小步数
}
if(flag)
{
for(i=;i<length;i++)
printf("%c",op[path[i]]); //根据path输出URLD路径
printf("\n");
}
}
else if(!nixu(map2)||!flag)
printf("unsolvable\n");
}
return ;
}
Eight(经典题,八数码)的更多相关文章
- hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数
题意: 题意就是八数码,给了一个3 * 3 的矩阵,上面有八个数字,有一个位置是空的,每次空的位置可以和他相邻的数字换位置,给你一些起始状态 ,给了一个最终状态,让你输出怎么变换才能达到目的. 思路: ...
- hdu1043Eight (经典的八数码)(康托展开+BFS)
建议先学会用康托展开:http://blog.csdn.net/u010372095/article/details/9904497 Problem Description The 15-puzzle ...
- hdu 1043 Eight 经典八数码问题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 The 15-puzzle has been around for over 100 years ...
- A*算法 -- 八数码问题和传教士过河问题的代码实现
前段时间人工智能的课介绍到A*算法,于是便去了解了一下,然后试着用这个算法去解决经典的八数码问题,一开始写用了挺久时间的,后来试着把算法的框架抽离出来,编写成一个通用的算法模板,这样子如果以后需要用到 ...
- Poj 1077 eight(BFS+全序列Hash解八数码问题)
一.题意 经典的八数码问题,有人说不做此题人生不完整,哈哈.给出一个含数字1~8和字母x的3 * 3矩阵,如: 1 2 X 3 4 6 7 5 8 ...
- HDU-1043 Eight八数码 搜索问题(bfs+hash 打表 IDA* 等)
题目链接 https://vjudge.net/problem/HDU-1043 经典的八数码问题,学过算法的老哥都会拿它练搜索 题意: 给出每行一组的数据,每组数据代表3*3的八数码表,要求程序复原 ...
- poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)
题目来源: http://poj.org/problem?id=1077 题目大意: 给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换.求一条路径,得到12345678x这样的矩阵 ...
- P1379 八数码naive题,STL的胜利
八数码:我使用了map判重 结果一遍就轻松A题了. 关于map的用法: ①创建一个map map<char,int>m; map<string,long long int>m1 ...
- HDU 1043 Eight 八数码问题 A*算法(经典问题)
HDU 1043 Eight 八数码问题(经典问题) 题意 经典问题,就不再进行解释了. 这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径. 解题思路 这里 ...
随机推荐
- java.lang.NoSuchFieldError:INSTANCE
Java.lang.NoSuchFieldError: INSTANCE异常,可能是包重复了. 我遇到的情况是maven里引入了一个JAR,而我又在lib里面引入了这个jar,并且版本还不相同,就出了 ...
- 如何设计好的RESTful API 之好的RESTful API 特征
原文地址:http://blog.csdn.net/ywk253100/article/details/25654021 导读:设计好RESTful API对于软件架构的可扩展性.可伸缩性和消费者的体 ...
- 新人补钙系列教程之:AS3 位运算符
ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数).在 ECMAScript 中,所有整数字面量默认都是有符号整数,这意味着什么呢? 有符号整数使用 3 ...
- linux fork的缺点
Disadvantage of fork linux环境下, JBoss中调用curl下载文件, 发现curl占用的内存和JBoss一样多. Historical Background and Pr ...
- 倍福TwinCAT(贝福Beckhoff)基础教程2.2 TwinCAT常见类型使用和转换_函数块
右击POUs,添加一个FB功能块,相比于FUN,FB功能块有INPUT,OUTPUT,还有VAR,即FB可以有多个输出,但是整个FB没有返回值 实现相同的功能,FB要比FUN难看的多,FB要声明实 ...
- 系统封装 如何加载PE到Easyboot进行合盘
1 直接使用别人的PE. 如何加载PE到Easyboot首先需要知道验证你下载的PE的ISO镜像能否启动 如果答案是可以的,以自由天空的MINIPE为例,虽然可以启动,但是完全没有菜单提示,我们想要作 ...
- 关于websocket和ajax无刷新
HTTP无状态: Ajax只能实现用户和服务器单方面响应(单工机制). 如果设置为长轮询(ajax设置多少秒进行一次请求,时间间隙可能会有延迟,且浪费资源) 如果设置为长连接(客户端请求一次,服务器保 ...
- WebGL 启动载入触发更新流程分析
WebGL 启动载入触发更新流程分析 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载 ...
- mysql kill process解决死锁
mysql使用myisam的时候锁表比较多,尤其有慢查询的时候,造成死锁.这时需要手动kill掉locked的process.使他释放. (以前我都是重起服务)..惭愧啊.. 演示:(id 7是我用p ...
- STL学习笔记(迭代器类型)
迭代器类型 迭代器是一种“能够遍历某个序列内的所有元素”的对象.它可以透过与一般指针一致的接口来完成自己的工作. 不同的迭代器具有不同的”能力“(行进和存取能力) Input迭代器 Input迭代器只 ...