数据库存储华容道求解过程分享

背景

之前有个网友问我要华容道求解代码,时过境迁,我已经没有存本了,这几天重写了一个,但考虑到不同网友的不同语言需求,我不能每个语言写一个代码吧,这不,我直接求解了所有的局面的所有答案,存于数据库中,大家可以通过访问数据库来查询求解过程,具体的使用方式我将在如下进行阐述。

建库

在数据库可视化工具中运行如下sql语句集,由于数据量有点大,在此写不下,我上传到了资源库中,访问链接如下
冰凌——华容道求解过程sql语句集

使用

数据库存储华容道求解过程分享_第1张图片

  1. 如上图所示,库中有两个表,分别是局面表和求解表,局面表存储了所有的局面,其有四个字段:
_id:序号,自增
_aspect:局面代号,空格、曹操、横将、竖将位置组成
_row:横将个数
_col:竖将个数

这里_aspect局面组成比较抽象,我详细描述一下,以横刀立马局面为例
数据库存储华容道求解过程分享_第2张图片
这是百度出来的图,【侵删】
上图中横将一个(关羽),竖将四个(其余五虎将),故其局面代表字段的
_row=1
_col=4
现在我们按照空格、曹操、横将、竖将的寻找顺序(以格子左上角的标号为主)来表示这个局面,给这个5X4的棋盘(从左到右、从上到下)分别标号为(0-19),则有以下表示:
0 , 1 , 2 , 3
4 , 5 , 6 , 7
8 , 9 , 10,11
12,13,14,15
16,17,18,19

空格位置:17、18
曹操位置:1
横将位置:9
竖将位置:0、3、8、11

那么局面代码应该是 17、18、1、9、0、3、8、11
但是这样不好看,于是我们将大于等于10的代号转变为大写字母,规则为A=10,B=11,…
0,1,2,3
4,5,6,7
8,9,A,B
C,D,E,F
G,H,I,J
如此,局面代码_aspect=HI19038B,这就是横刀立马的代码,我们在数据库的局面表中查询一下

SELECT * FROM huarongdao.tbl_aspect where _aspect='HI19038B' and _row=1 and _col=4;

数据库存储华容道求解过程分享_第3张图片
得到其代号为80068,然后我们再到求解表中查询其求解过程

select * from tbl_solution where _id=80068;

查询结果如下:

_id   _step _x  _y  _direction
80068	1	3	180068	2	4	380068	3	2	380068	4	2	180068	5	2	080068	6	4	080068	7	4	180068	8	2	180068	9	2	2	←←
80068	10	3	280068	11	4	2	↑↑
80068	12	3	180068	13	3	080068	14	2	080068	15	2	2	←←
80068	16	2	3	←←
80068	17	3	280068	18	3	380068	19	4	1	→→
80068	20	4	0	→→
80068	21	3	080068	22	2	180068	23	2	280068	24	2	380068	25	0	3	↓↓
80068	26	0	180068	27	0	080068	28	2	0	↑↑
80068	29	3	0	↑↑
80068	30	2	180068	31	0	1	↓↓
80068	32	0	280068	33	2	3	↑↑
80068	34	2	280068	35	4	2	↑↑
80068	36	4	380068	37	4	0	→→
80068	38	2	180068	39	2	080068	40	2	2	←←
80068	41	0	180068	42	0	0	→→
80068	43	1	080068	44	2	0	↑↑
80068	45	3	0	↑↑
80068	46	3	180068	47	3	280068	48	1	180068	49	0	280068	50	0	380068	51	2	3	↑↑
80068	52	2	180068	53	1	180068	54	0	180068	55	0	080068	56	1	080068	57	3	080068	58	4	180068	59	2	1	↓↓
80068	60	2	280068	61	0	3	↓↓
80068	62	0	280068	63	0	180068	64	1	180068	65	0	080068	66	2	0	↑↑
80068	67	2	180068	68	1	2	↓↓
80068	69	0	2	↓↓
80068	70	0	380068	71	2	3	↑↑
80068	72	3	280068	73	4	280068	74	4	1	→→
80068	75	4	0	→→
80068	76	2	080068	77	2	2	←←
80068	78	2	3	←←
80068	79	3	280068	80	4	280068	81	3	0

其中
_id与局面表中_id相对应,是外键
_step为此局面的第i步
_x为此局面第i步要移动的格子的横坐标(从0开始)
_y为此局面第i步要移动的格子的纵坐标(从0开始)
_direction为此局面第i步要移动的格子的移动方向
此为求解某局面的方法

  1. 总共有多少不同局面(对称和中心对称等局面视为不同)
    数据库存储华容道求解过程分享_第4张图片
  2. 最复杂的局面
select max(_step) from tbl_solution;
得到
max(_step)
138

select _id from tbl_solution where _step=138;
得到
_id
174309
184519
185561
185745
190051
190223

select * from tbl_aspect where _id in(select _id from tbl_solution where _step=138);
得到
_id     _aspect    _row _col
174309	CG4DI36B	2	3
184519	FJ6DG058	2	3
185561	GH4DI36B	2	3
185745	GH6DI058	2	3
190051	IJ4DG36B	2	3
190223	IJ6DG058	2	3
			
  1. 无解或不需要解的局面的个数
select count(*) from tbl_aspect where _id not in(select distinct _id from tbl_solution);
得到
count(*)
121145

结语与扩展

相信大家只要熟悉sql语句就可以从中获取需要的信息,熟悉语言和数据库交互就可以运用自如,有兴趣自己实现搜索算法的同学,我这里给出伪代码,提高参考:
华容道总共10个方块,四种类型,其中1个2X2,5个1X2【分为横放竖放两种】,4个1X1,我们可以分别定义四种枚举类型,本人英文不大好,这里就用拼音代替了

//方格类型枚举
enum BlockType{
	KONGDI,   //空地
	CAOCAO,   //曹操
	HENGJIANG,//横将
	SHUJIANG, //竖将
	XIAOBING  //小兵
};

枚举移动方向

//移动方向枚举
enum Direction{
	UP,
	DOWN,
	LEFT,
	RIGHT,
	UPUP,
	DOWNDOWN,
	LEFTLEFT,
	RIGHTRIGHT,
	UPLEFT,
	UPRIGHT,
	DOWNLEFT,
	DOWNRIGHT
};

定义方格类型

//方格类
struct Block{
	int x;		//横坐标
	int y;		//纵坐标
	int width;	//宽度
	int height;	//高度
	int type;	//类型
};

定义局面类

//局面类
struct Aspect{
	Block blocks[10];		//方格数组
	Aspect *lastAspect;		//上个局面
	int x;					//上个局面移动的方格的横坐标
	int y;					//上个局面移动的方格的纵坐标
	int direction;			//上个局面移动的方格的方向
}

广搜算法
以下伪代码仅供参考,请勿当作源码复制进编辑器中编译运行!!!
以上伪代码仅供参考,请勿当作源码复制进编辑器中编译运行!!!
以上伪代码仅供参考,请勿当作源码复制进编辑器中编译运行!!!

queue<Aspect> q;	//队列q存局面
set<string> s;		//集合s存局面代码,方便去重

q.push(firstAspect);//挤入初始待解局面
s.add(firstAspect.toCode());//存储局面代码,toCode方法自己实现,实现原理如上边的_aspect的形成

//开始求解
while(q.size()>0){	//队列非空
	Aspect now=q.pop();	//出队并获取元素
	
	//遍历当前局面中的所有方格
	foreach(block in now.blocks){
		//遍历每个方向
		foreach(direction){
			if(block 可以 向 direction 方向移动){
				//尝试移动
				Aspect next;
				next.blocks=now.blocks;
				next的对应block向direction移动;
				next.lastAspect=now;
				next.x=block.x;
				next.y=block.y;
				next.direction=direction;
			
				//判断是否解决了问题,即曹操是否到达坐标[3,1]
				if(next.isSuccess()){
					打印过程;
					return;
				}
	
				//获取形成代码
				string code=next.toCode();

				//该局面出现过,不做任何操作
				if(s.contains(code)){
					pass;
				//该局面第一次出现,加入队列中,之后继续求解
				}else{
					q.push(next);
					s.add(code);
				}
			}
		}
	}
}

打印局面无解;

你可能感兴趣的:(数据库,算法,数据库,算法,mysql)