POJ 1077 Eight

来源:互联网 发布:虎牙直播软件 编辑:程序博客网 时间:2024/06/06 01:16

题目大意:

        只有一个测例,求解八数码问题,输入的是初始的八数码摆放位置,以一行显示,顺序是从上到下(行)从左到右显示,数字是1 ~ 8,空格用x表示,目标状态为排列1 ~ 8 + x,输出为空移动的方向,l、r、u、d的序列(无空格)表示左右上下。

题目链接

A*:

注释代码:

/*   * Problem ID : POJ 1077 Eight * Author     : Lirx.t.Una   * Language   : G++   * Run Time   : 32 ms   * Run Memory : 3188 KB  */  #pragma G++ optimize("O2")#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <queue>//表示数码个数#defineN8//string length//这里用字符串来存放数码状态//加上x总共有9块砖//最后一位留给'\0'//因此长度为10#defineSTRLEN10//maximum factorial number//最大阶乘号//由于有九个数,因此对应着9!种状态//将不同的数码状态利用康托展开映射到9!个数//91 = 362880#defineMAXFAC362880//maximum length of path//即x移动的路径长度//最大值经测试为25#defineMAXPATHLEN25//总共有4中x的移动方法,即上下左右#defineMOVN4//康托展开中所用到的阶乘个数//由于数码中最大数字为9(将x转换为9)//因此最大阶乘为(9-1)!//阶乘从0!开始一直到8!//因此为9中阶乘#define FCN9using namespace std;structNode {//每个结点存放一种数码状态//由于将每种状态都转化成一串1~9的字符了(x转换为9)//因此可以组成一个9位数,没有超出int范围//用int存比直接用字符串存节省空间(要考虑结构体数据对齐的情况)ints;//即以int的形式数码字符串//以下为A*的估价函数的参数://f = g + h//g为解空间树中到达当前位置的代价(即步数,根结点代价为0,因此即为边的数量)    //h为当前状态到目标状态所要付出的代价,则个值一般以最小值计  //由于这里没有精确的最小值计算方法,因此只能用一些算法做近似  //这里h用h_val函数求得intg;intf;Node(void) {}Node( int ss, int gg, int hh ) :s(ss), g(gg), f( gg + hh ) {}//hh传参时就已经用h_val求出了booloperator<(const Node &oth)//使用有限队列,让f最小者一直处于堆顶const {return f > oth.f;}};structFath {//Father Node//父结点//这里利用数组模拟解空间树//fath[i]就表示阶乘号为i的结点的父结点//里面存放了父结点的阶乘号fac//以及父结点到当前子结点的走法(lrud)movmentintfac;charmov;};priority_queue<Node>q;//queue,利用STL的priority_queue来模拟优先队列(小顶堆)Fathfath[MAXFAC];//解空间树boolvist[MAXFAC];//have visited?vist[i]表示阶乘号为i的结点是否被访问过charpath[MAXPATHLEN];//从根结点触发到达目标结点过程中x所走过的路径                         //即此问题的答案//在移动x的时候将其动作转化到二维空间中的上下左右//由于坐标系是朝右下角的,所以x正半轴朝下,y正半轴朝右//(dx, dy)为x的移动向量//分别对应着上下左右四种移动方式intdx[MOVN] = { 0, 0, -1, 1 };intdy[MOVN] = { -1, 1, 0, 0 };charmv[MOVN] = { 'l', 'r', 'u', 'd' };//对应着0!~8!这9种阶乘intFC[FCN] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };boolcnsov(char *s) {//can solve this problem//测试所给的初始状态能否到达目标状态//目标状态就是排列123456789//方法是求初始状态的逆序数//注意!不包括空格x的序列的逆序数inti, j;intord;//ordinal number,即逆序数的结果保存其中for ( ord = 0, i = 0; i < N; i++ )if ( '9' != s[i] )for ( j = i + 1; j <= N; j++ )if ( '9' != s[j] && s[i] > s[j] )ord++;if ( ord % 2 )//如果逆序数为奇数则不可解return false;return true;//否则可解}inth_val(char *s) {//get h_value求估价函数的参数hinti;inth;//这里采用当前状态中每个格子的值和目标状态中每个对应格子中的绝对差值的方法//求和的方法估算h参数//对于当前状态中i号格子中放的数就是s[i]//而目标状态中i号格子中放的数是(i+'1')for ( h = 0, i = 0; i <= N; i++ )h += abs(s[i] - '1' - i);return h;}inthash(char *s) {//康托展开,即一个哈希函数//将给定状态所表示的字符串映射到一个阶乘值上//可以使每个状态都有不同的阶乘号inti, j;intord;//每个i号位置数所拥有的逆序数intfac;//最终的字符串哈希值,即阶乘号for ( fac = 0, i = 0; i < N; i++ ) {for ( ord = 0, j = i + 1; j <= N; j++ )if ( s[i] > s[j] )ord++;//康托展开的核心求法//每一轮的逆序数乘以各自的阶乘数fac += FC[ s[i] - '1' ] * ord;}return fac;}voidfndx( char *s, int &x, int &y ) {//find x//找到状态中x的位置并求其二位坐标inti;for ( i = 0; '9' != s[i]; i++ );x = i / 3;//横坐标y = i % 3;//纵坐标}voidswp( char *s, int i, int j ) {//swap//交换字符串中为位置i和位置j的字符chartmp;tmp = s[i];s[i] = s[j];s[j] = tmp;}inta_star(char *sini) {//A*算法//init string,根结点状态对应的字符串Nodenfath;//father node,父结点charsfath[STRLEN];//father string,父结点状态对应的字符串charschld[STRLEN];//children string,子结点对应的字符串intfc;//children factorial number,子结点对应的阶乘号//(x, y)为父结点状态中x的坐标//(xx, yy)为子结点状态中x的坐标intx, xx;inty, yy;inti;//计数变量q.push( Node( atoi(sini), 0, h_val(sini) ) );//先将根结点入队vist[ hash(sini) ] = true;//并将根结点置为访问过while ( true ) {//由于在main函数中已经先用cnsov函数判断过问题的可解性了//因此这里就默认问题是可解的//一定能return出a_star函数,因此这里就做了一个死循环//先弹堆获得一个父结点nfath = q.top();q.pop();//取出父结点的状态字符串并找到x的位置sprintf(sfath, "%d", nfath.s);fndx(sfath, x, y);for ( i = 0; i < MOVN; i++ ) {//依次按照四种方向生成//父结点的子结点//子结点中x的位置是由父结点中x上下左右移动得来的xx = x + dx[i];yy = y + dy[i];//1. 如果x走出了3×3框架范围则直接排除考察范围if ( xx < 0 || xx > 2 || yy < 0 || yy > 2 )continue;//构造子结点的状态字符串strcpy(schld, sfath);swp( schld, 3 * x + y, 3 * xx + yy );//2. 如果子结点已经被访问过了则也直接排除考察范围if ( vist[ fc = hash(schld) ] )//必须通过获得子结点的阶乘号//才可以获得它的vist的信息continue;//如果没有访问过则需要对其进行详细考察vist[fc] = true;//将其置为访问过fath[fc].fac = hash(sfath);//设置子结点的父结点的阶乘号fath[fc].mov = mv[i];//从父结点走到子结点的方法也需设置if ( !fc )//如果当前生成的子结点已经是目标结点了//目标结点123456789的阶乘值为0(因为无逆序数)return nfath.g + 1;//则直接返回从初始状态到目标状态的代价(即步数)//否则需要将子结点入队待下次有机会再考察q.push( Node( atoi(schld), nfath.g + 1, h_val(schld) ) );}}}intmain() {charsini[STRLEN];//初始状态字符串charc;//每次接受一个字符inti;//计数变量for ( i = 0; i <= N; i++ ) {scanf("%s", &c);if ( 'x' == c )sini[i] = '9';elsesini[i] = c;}sini[i] = '\0';if ( !cnsov(sini) ) {puts("unsolvable");return 0;}intfc;//children factorial number,子结点阶乘号Fath *pf;//pointer to father node,父结点指针i = a_star(sini);//i此时为path的长度//从0到i-1存放完整路径path[i--] = '\0';//左后以为放空字符fc = 0;//子结点指针初始化为0,即目标结点的阶乘号pf = fath;//父结点指针初始化为目标结点的父节点//从后往前构造pathwhile ( i >= 0 ) {path[i--] = pf->mov;//父结点到子结点的走法fc = pf->fac;//将子结点上移为父结点pf = fath + fc;//将父结点上移为父父结点,即更新完的子结点的父结点}puts(path);//输出return 0;}

无注释代码:

#pragma G++ optimize("O2")#include <iostream>#include <cstring>#include <cstdlib>#include <cstdio>#include <queue>#defineN8#defineSTRLEN10#defineMAXFAC362880#defineMAXPATHLEN25#defineMOVN4#define FCN9using namespace std;structNode {ints;intg;intf;Node(void) {}Node( int ss, int gg, int hh ) :s(ss), g(gg), f( gg + hh ) {}booloperator<(const Node &oth)const {return f > oth.f;}};structFath {intfac;charmov;};priority_queue<Node>q;Fathfath[MAXFAC];boolvist[MAXFAC];charpath[MAXPATHLEN];intdx[MOVN] = { 0, 0, -1, 1 };intdy[MOVN] = { -1, 1, 0, 0 };charmv[MOVN] = { 'l', 'r', 'u', 'd' };intFC[FCN] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };boolcnsov(char *s) {inti, j;intord;for ( ord = 0, i = 0; i < N; i++ )if ( '9' != s[i] )for ( j = i + 1; j <= N; j++ )if ( '9' != s[j] && s[i] > s[j] )ord++;if ( ord % 2 )return false;return true;}inth_val(char *s) {inti;inth;for ( h = 0, i = 0; i <= N; i++ )h += abs(s[i] - '1' - i);return h;}inthash(char *s) {inti, j;intord;intfac;for ( fac = 0, i = 0; i < N; i++ ) {for ( ord = 0, j = i + 1; j <= N; j++ )if ( s[i] > s[j] )ord++;fac += FC[ s[i] - '1' ] * ord;}return fac;}voidfndx( char *s, int &x, int &y ) {inti;for ( i = 0; '9' != s[i]; i++ );x = i / 3;y = i % 3;}voidswp( char *s, int i, int j ) {chartmp;tmp = s[i];s[i] = s[j];s[j] = tmp;}inta_star(char *sini) {Nodenfath;charsfath[STRLEN];charschld[STRLEN];intfc;intx, xx;inty, yy;inti;q.push( Node( atoi(sini), 0, h_val(sini) ) );vist[ hash(sini) ] = true;while ( true ) {nfath = q.top();q.pop();sprintf(sfath, "%d", nfath.s);fndx(sfath, x, y);for ( i = 0; i < MOVN; i++ ) {xx = x + dx[i];yy = y + dy[i];if ( xx < 0 || xx > 2 || yy < 0 || yy > 2 )continue;strcpy(schld, sfath);swp( schld, 3 * x + y, 3 * xx + yy );if ( vist[ fc = hash(schld) ] )continue;vist[fc] = true;fath[fc].fac = hash(sfath);fath[fc].mov = mv[i];if ( !fc )return nfath.g + 1;q.push( Node( atoi(schld), nfath.g + 1, h_val(schld) ) );}}}intmain() {charsini[STRLEN];charc;inti;for ( i = 0; i <= N; i++ ) {scanf("%s", &c);if ( 'x' == c )sini[i] = '9';elsesini[i] = c;}sini[i] = '\0';if ( !cnsov(sini) ) {puts("unsolvable");return 0;}intfc;Fath *pf;i = a_star(sini);path[i--] = '\0';fc = 0;pf = fath;while ( i >= 0 ) {path[i--] = pf->mov;fc = pf->fac;pf = fath + fc;}puts(path);return 0;}

IDA*:

注释代码:

无注释代码:

单词解释:

sliding:adj, 可滑动的,变化的

tile:n, 瓦片,瓷砖

pack:vt, 包装,塞满,捆扎

frame:n, 框架

object:n, 目标

scramble:vt, 使混杂,搅乱,攀登

slightly:adv, 稍微地,轻微地

letter:n, 字母

row:n, 行,排

Sam Loyd:人名,山姆·劳埃德,历史上第一个构造出不可解的15数码问题

frustrate:vt, 挫败,使感到灰心

compose of:由...组成

configuration:n, 配置,结构

0 0
原创粉丝点击