大一结束,为了检验自身实力,专业内的任务是实现华容道最快求解。这里我只针对华容道经典棋局--横刀立马进行分析。
一.棋子设置
华容道棋子 主要有五种:曹操,横将,竖将,小兵,空格。对于移动的方法,有两种:一,移动空格;二,移动棋子。相对于效率来说,移动空格会更快一点,我才用的就是移动空格。至于设置棋盘,可以建立节点存储一个棋盘,棋盘可以有三种形式:二维数组,一维数组,一个long long int的数(使用位运算,一个long long int有64个位,每个棋子用三个位表示);对于新手,使用一维数组会比较好理解,因为一维数组与二维数组差不多,但是二维数组很容易炸,所以不推荐,至于位运算可以在一维的基础上进行改造,方便快捷。
0 马超 | 1曹操 | 2曹操 | 3赵云 |
4 马超 | 5曹操 | 6曹操 | 7赵云 |
8黄忠 | 9关羽 | 10关羽 | 11张飞 |
12黄忠 | 13小兵 | 14小兵 | 15张飞 |
16小兵 | 17空 | 18空 | 19小兵 |
那么在进行移动的时候就会有一点小麻烦,就是在判断是否会超出界限,所以我又定义了两个数组,将一维数组做一个类似二维数组的处理,使其更好理解
class Node
{
public:
char chess[21];//棋盘
int step;//步数统计器
Node *pNext;//回溯指针
Node() {}
Node(char chess[21]) :pNext(nullptr)
{
for (int i = 0; i < 20; i++)
{
this->chess[i] = chess[i];
this->pNext = pNext;
}
}
void dispaly()
{
for (int i = 0; i < 20; i++)
{
cout << chess[i];
if ((i + 1) % 4 == 0)
{
cout << endl;
}
}
}
};
在设置棋子时,可以将将军棋设置为如1,1;2,2;3,3;小兵用6,7,8,9表示,空格用0表示,类似的操作对其他棋子进行规划:
int pos1[20] = { 0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3 };//表示列坐标
int pos2[20] = { 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4 };//表示横坐标
1 | x | x | 2 |
1 | x | x | 2 |
3 | 5 | 5 | 4 |
3 | 6 | 7 | 4 |
8 | 0 | 0 | 9 |
这样做的话,会缺少一个数字来表示曹操,所以并不太好,但是也可用,后面我将介绍一种剪枝方法,会更好一些。
二.移动
移动方法其实比较好理解,首先需要进入一遍循环,遍历查找到空格位置,然后判断两个空格是否在一起,在一起又分为横向连接及竖向连接。
int x = 0, y = 0; int l = 0;
for (int i = 0; i < 20; i++)
{
if (n.chess[i] == '0')
{
x = y;
y = i;
}
}
if (x + 4 == y)
{
l = 1;//竖联合
}
if (x + 1 == y && pos1[x] != 3)
{
l = 2;//横联合
}
如果相连接,在移动的时候需要将联合空格一起上下左右移动一遍,注意只判断可走情况,例如横空上移,只判断其上方是否为横将或曹操。联合移动完之后再单独针对联合空格中的每一个单空格分别进行上下左右移动。注意:(不管空格是否联合,都要分别移动单空格。 )也就是使用穷举法进行走步,虽然代码长度比走兵长,而且逻辑烦,但是不得不承认的是他的效率确实比较快,在后期位运算至少领先走兵5ms。这个代码是检验能力的一步,所以最好自己尝试实现一遍,毕竟不难。在移动的时候可以使用swap来交换两个位置的值,但是如果要追求效率的话,我建议直接赋值,这样会更快一些。
for (int i = 0; i < 20; i++)
{
if (n.chess[i] == '0')
{
x = y;
y = i;
}
}
if (x + 4 == y)
{
l = 1;//竖联合
}
if (x + 1 == y && pos1[x] != 3)
{
l = 2;//横联合
}
void Move(Node n, queue<Node> &q)
{
if (l == 1)//竖联合
{
if (pos1[x] > 0)//向上
{
if (n.chess[x - 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 4] = '0'; n1->chess[y] = '2';
insertq(n, *n1, q);
}
else if (n.chess[x - 4] == '3')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '1'; n1->chess[x - 8] = '0';
n1->chess[y] = '3'; n1->chess[x - 4] = '0';
insertq(n, *n1, q);
}
}
if (pos1[x] < 3)//向下
{
if (n.chess[y + 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[y + 4] = '0';
insertq(n, *n1, q);
}
else if (n.chess[y + 4] == '1')
{
Node *n1 = new Node(n.chess);
n1->chess[y + 4] = '0'; n1->chess[y + 8] = '0';
n1->chess[x] = '1'; n1->chess[y] = '3';
insertq(n, *n1, q);
}
}
if (pos[x] > 0)//向左
{
if (n.chess[x - 1] == '1')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 1] = '0'; n1->chess[y - 1] = '0';
n1->chess[x] = '1'; n1->chess[y] = '3';
insertq(n, *n1, q);
}
else if (n.chess[x - 1] == '6'&&n.chess[y - 1] == '6')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 2] = '0'; n1->chess[y - 2] = '0';
n1->chess[x] = '6'; n1->chess[y] = '6';
insertq(n, *n1, q);
}
if (n.chess[x - 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[y] = '2'; n1->chess[x - 1] = '0';
insertq(n, *n1, q);
}
if (n.chess[y - 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[y - 1] = '0';
n1->chess[x] = '2';
insertq(n, *n1, q);
}
}
if (pos[x] < 3)//向右
{
if (n.chess[x + 1] == '1')
{
Node *n1 = new Node(n.chess);
n1->chess[x + 1] = '0'; n1->chess[y + 1] = '0';
n1->chess[x] = '1'; n1->chess[y] = '3';
insertq(n, *n1, q);
}
else if (n.chess[x + 1] == '6'&&n.chess[y + 1] == '6')
{
Node *n1 = new Node(n.chess);
n1->chess[x + 2] = '0'; n1->chess[y + 2] = '0';
n1->chess[x] = '6'; n1->chess[y] = '6';
insertq(n, *n1, q);
}
if (n.chess[x + 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x + 1] = '0'; n1->chess[y] = '2';
insertq(n, *n1, q);
}
if (n.chess[y + 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[y + 1] = '0'; n1->chess[x] = '2';
insertq(n, *n1, q);
}
}
}
//pos列pos1行
else if (l == 2)//横联合
{
if (pos1[x] > 0)//向上
{
if (n.chess[x - 4] == '5')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 4] = '0'; n1->chess[y - 4] = '0';
n1->chess[x] = '5'; n1->chess[y] = '7';
insertq(n, *n1, q);
}
else if (n.chess[x - 4] == '6'&&n.chess[y - 4] == '6')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 8] = '0'; n1->chess[y - 8] = '0';
n1->chess[x] = '6'; n1->chess[y] = '6';
insertq(n, *n1, q);
}
if (n.chess[x - 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[y] = '2'; n1->chess[x - 4] = '0';
insertq(n, *n1, q);
}
if (n.chess[y - 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[y - 4] = '0';
insertq(n, *n1, q);
}
}
if (pos1[x] < 4)//向下
{
if (n.chess[x + 4] == '5')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '5'; n1->chess[y] = '7';
n1->chess[x + 4] = '0'; n1->chess[y + 4] = '0';
insertq(n, *n1, q);
}
else if (n.chess[x + 4] == '6'&&n.chess[y + 4] == '6')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '6'; n1->chess[y] = '6';
n1->chess[x + 8] = '0'; n1->chess[y + 8] = '0';
insertq(n, *n1, q);
}
if (n.chess[x + 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x + 4] = '0'; n1->chess[y] = '2';
insertq(n, *n1, q);
}
if (n.chess[y + 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[y + 4] = '0';
insertq(n, *n1, q);
}
}
if (pos[x] > 0)//向左
{
if (n.chess[x - 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x + 1] = '2'; n1->chess[x - 1] = '0';
insertq(n, *n1, q);
}
else if (n.chess[x - 1] == '7')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 1] = '0'; n1->chess[x - 2] = '0';
n1->chess[x] = '5'; n1->chess[y] = '7';
insertq(n, *n1, q);
}
}
if (pos[y] < 3)//向右
{
if (n.chess[y + 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[y + 1] = '0';
insertq(n, *n1, q);
}
else if (n.chess[y + 1] == '5')
{
Node *n1 = new Node(n.chess);
n1->chess[y + 2] = '0'; n1->chess[y + 1] = '0';
n1->chess[x] = '5'; n1->chess[y] = '7';
insertq(n, *n1, q);
}
}
}
for (int i = 0; i < 2; i++)
{
if (pos1[x] > 0)//向上
{
if (n.chess[x - 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 4] = '0'; n1->chess[x] = '2';
insertq(n, *n1, q);
}
else if (n.chess[x - 4] == '3')
{
Node *n1 = new Node(n.chess);
n1->chess[x - 8] = '0'; n1->chess[x - 4] = '1'; n1->chess[x] = '3';
insertq(n, *n1, q);
}
}
if (pos1[x] < 4)//向下
{
if (n.chess[x + 4] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2';
n1->chess[x + 4] = '0'; insertq(n, *n1, q);
}
else if (n.chess[x + 4] == '1')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '1'; n1->chess[x + 4] = '3'; n1->chess[x + 8] = '0';
insertq(n, *n1, q);
}
}
if (pos[x] > 0)//向左
{
if (n.chess[x - 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[x - 1] = '0';
insertq(n, *n1, q);
}
else if (n.chess[x - 1] == '7')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '7'; n1->chess[x - 1] = '5'; n1->chess[x - 2] = '0';
insertq(n, *n1, q);
}
}
if (pos[x] < 3)//向右
{
if (n.chess[x + 1] == '2')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '2'; n1->chess[x + 1] = '0';
insertq(n, *n1, q);
}
else if (n.chess[x + 1] == '5')
{
Node *n1 = new Node(n.chess);
n1->chess[x] = '5'; n1->chess[x + 1] = '7'; n1->chess[x + 2] = '0';
insertq(n, *n1, q);
}
}
x = y;
}
}
记得每次移动前要先将棋盘复制一下,以免改变父代,使后续的子代出错,所以切记一定要复制棋盘。
三.算法
在华容道中,主要使用的算法有两个:广度搜索,哈希算法。
广度搜索的目的是为了找出最后棋局,
棋盘的拓展开后是一棵树,bfs算法的作用就是将这一棵树串成一个队列(也可以用栈),也就是把每一代拓展出来的子代都连接在队列的末端(q.push()),每当队列的头节点已经开拓完毕,就需要将头节点删除掉(q.pop()),达到一个广度搜索的目的。
while (!q.empty())
{
Move(q.front(), q);//移动方法
q.pop();//出队
if (wins)//判断是否获胜
{
break;
}
}
插入队列
n1.step = n.step + 1;//子节点的步数为父节点的步数加一
n1.pNext = &n;//子节点内的指针指向父代
q.push(n1);//入队
至于哈希算法:哈希算法的作用是巡查是否出现相同格局,因为如果缺少这个步骤,那么整个移动将会十分混乱,并且可能用了许久的时间后却回到初始格局,那么就造成浪费时间,也有很大几率(>90%)跑不出答案。哈希算法对于新手来说确实太难,要完整写出一个哈希并不容易,所以我这里给大家推荐一个好东西(unordered_map),这个map内部使用的也是哈希,但是相对于自己写的哈希算法确实比较低效的,但不失为一个好方法,对于新手比较容易接受。(当然有能力的大佬当然也可以选择自己写一个哈希,主要使用的就是折叠法转化成哈希数,然后建立一个使用拉链法的开散列表,注意折叠时取模采用的数最好是素数,尽量多次尝试找到一个较合适的数字,使数据足够分散,以此提高查找效率)
使用unordered_map首先要先定义一个这个类型的变量(unordered_map<string, int> hrd;),像这样,尖括号里有两个类型,第一个类型为关键字,相当于数组的下标,第二个为这个空间内的元素,如果像我一样定义为int,那么他就可以存下一个int型的元素,如 1,
这样进行检查后就可以避免往回走的情况出现。
bool Thesame(string s)
{
if (hrd[s] == 0)//是否出现过
{
hrd[s] = 1;//标记为出现过
return false;
}
else
{
return true;
}
}
备注:听说曾经有同学使用dfs进行拓展,关于dfs,在这里是很不适用的,所以最好不要用,可以跑出答案,但是效率真的很低,小辣鸡的我强烈不推荐。
四.剪枝
剪枝的话,首先就是对棋子进行缩减,也就是一开始提到的将棋子分类,这样可以提高效率;
关于棋子的设置的话,我做了一点简化:竖将同意用1,3表示,1是头,3是尾,横将用5,7,小兵用2,空格用0,曹操用6(请扣666,谢谢),这样会比较简单,将不同人物分别做成不同棋子实在没有必要。
!其次,对查重进行剪枝。一个棋局在电脑数据中与他的左右镜像棋盘并没有差别,所以在每一次的查重的过程中可以顺便检查他的镜像棋盘,避免同样棋盘的出现,以此提高效率。
1 | 6 | 6 | 1 |
3 | 6 | 6 | 3 |
1 | 5 | 7 | 1 |
3 | 2 | 2 | 3 |
2 | 0 | 0 | 2 |
string Mirror(string s)
{
string o;
for (int i = 0; i < 5; i++)
{
for (int x = 0; x < 4; x++)
{
o += s[(4 * i) + 3 - x];
}
}
for (int i = 0; i < 20; i++)
{
if (o[i] == '7')
{
o[i] = '5';
o[i + 1] = '7';
i++;
}
}
return o;
}
五.位运算优化
位运算只要是在移动棋子的时候使用,也就是赋值时使用位运算。
一个long long int型的数,用二进制表示的话,有64位,一个long long int的数表示一个棋盘,每一个格子用三个位来表示,一共有二十个空,也就是要用到60位,我的做法是三位三位地压进这个long long int里面
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
二十 | 十九 | 十八 | 十七 | 十六 | 十五 | 十四 | 十三 | 十二 | 十一 | 十 | 九 | 八 | 七 | 六 | 五 | 四 | 三 | 二 | 一 |
Node(long long int a[20])//初始使用一个long long int 型数组存下棋盘(也可以采用输入或者文件读入)
{
for (int i = 0; i < 20; i++)
{
l = l | ((long long int)a[i] << i * 3);//从右往左逐步压入
}
}
这样子就可以用一个long long int的数当作一个棋盘。
在移动的时候,也就是一个赋值过程中,如果要把某一空格的值改掉,只需要通过^,| 两个符号使用恰当的计算将该位置替换掉即可,如,有个位置的数是7,二进制表示为111,想要将他改变成5,只需要将long long int型的2推到该位置,然后只用异或的操作(^)就可以得到101,因为2是010,异或就是两个位置的不同才为1,否则统统为0,所以可以满足该操作,将5变成7的时候也可以使用异或来进行改变;如果某个位置是0,想要将他变成1的话,只需要将long long int型的1推到该位置,然后采用或的操作(|)将其改变,0用二进制表示为000,1为001,或的作用就是两个位置只要有一个为1就为1,所以可以满足该操作;如果某个位置为3,想将他变成0,则要异或上他自己,3用二进制表示为011,异或上011的话,三个位就都变成0了,也就成功实现了要求。
void Move(Node n, queue<Node> &q)
{
int x = 0, y = 0; int l = 0;
for (int i = 0; i < 20; i++)
{
if (((n.l >> (i * 3)) & 7) == 0)
{
x = y;
y = i;
}
}
if (x + 4 == y)
{
l = 1;//竖联合
}
if (x + 1 == y && pos[x] != 3)
{
l = 2;//横联合
}
if (l == 1)//竖联合
{
if (pos1[x] > 0)
{
if (((n.l >> ((x - 4) * 3)) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((x - 4) * 3));
n1->l = (n1->l) | (p[2] << (y * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 4) * 3) & 7) == 3)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[1] << (x * 3));
n1->l = (n1->l) ^ (p[1] << ((x - 8) * 3));
n1->l = (n1->l) | (p[3] << (y * 3));
n1->l = (n1->l) ^ (p[3] << ((x - 4) * 3));
insertq(n, *n1, q);
}
}
if (pos1[x] < 3)
{
if ((n.l >> ((y + 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ ((p[2] << (y + 4) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((y + 4) * 3) & 7) == 1)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[1] << ((y + 4) * 3));
n1->l = (n1->l) ^ (p[3] << ((y + 8) * 3));
n1->l = (n1->l) | (p[1] << (x * 3));
n1->l = (n1->l) | (p[3] << (y * 3));
insertq(n, *n1, q);
}
}
if (pos[x] > 0)
{
if ((n.l >> ((x - 1) * 3) & 7) == 1)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[1] << ((x - 1) * 3));
n1->l = (n1->l) ^ (p[3] << ((y - 1) * 3));
n1->l = (n1->l) | (p[1] << (x * 3));
n1->l = (n1->l) | (p[3] << (y * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 1) * 3) & 7) == 6 && (n.l >> ((y - 1) * 3) & 7) == 6)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[6] << ((x - 2) * 3));
n1->l = (n1->l) ^ (p[6] << ((y - 2) * 3));
n1->l = (n1->l) | (p[6] << (x * 3));
n1->l = (n1->l) | (p[6] << (y * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((x - 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (y * 3));
n1->l = (n1->l) ^ (p[2] << ((x - 1) * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((y - 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((y - 1) * 3));
n1->l = (n1->l) | (p[2] << (x * 3));
insertq(n, *n1, q);
}
}
if (pos[x] < 3)
{
if ((n.l >> ((x + 1) * 3) & 7) == 1)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[1] << ((x + 1) * 3));
n1->l = (n1->l) ^ (p[3] << ((y + 1) * 3));
n1->l = (n1->l) | (p[1] << (x * 3));
n1->l = (n1->l) | (p[3] << (y * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x + 1) * 3) & 7) == 6 && (n.l >> ((y + 1) * 3) & 7) == 6)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[6] << ((x + 2) * 3));
n1->l = (n1->l) ^ (p[6] << ((y + 2) * 3));
n1->l = (n1->l) | (p[6] << (x * 3));
n1->l = (n1->l) | (p[6] << (y * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((x + 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((x + 1) * 3));
n1->l = (n1->l) | (p[2] << (y * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((y + 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((y + 1) * 3));
n1->l = (n1->l) | (p[2] << (x * 3));
insertq(n, *n1, q);
}
}
}
//pos列pos1行
else if (l == 2)//横联合
{
if (pos1[x] > 0)
{
if ((n.l >> ((x - 4) * 3) & 7) == 5)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[5] << ((x - 4) * 3));
n1->l = (n1->l) ^ (p[7] << ((y - 4) * 3));
n1->l = (n1->l) | (p[5] << (x * 3));
n1->l = (n1->l) | (p[7] << (y * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 4) * 3) & 7) == 6 && (n.l >> ((y - 4) * 3) & 7) == 6)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[6] << ((x - 8) * 3));
n1->l = (n1->l) ^ (p[6] << ((y - 8) * 3));
n1->l = (n1->l) | (p[6] << (x * 3));
n1->l = (n1->l) | (p[6] << (y * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((x - 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (y * 3));
n1->l = (n1->l) ^ (p[2] << ((x - 4) * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((y - 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((y - 4) * 3));
insertq(n, *n1, q);
}
}
if (pos1[x] < 4)
{
if ((n.l >> ((x + 4) * 3) & 7) == 5)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[5] << (x * 3));
n1->l = (n1->l) | (p[7] << (y * 3));
n1->l = (n1->l) ^ (p[5] << ((x + 4) * 3));
n1->l = (n1->l) ^ (p[7] << ((y + 4) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x + 4) * 3) & 7) == 6 && (n.l >> ((y + 4) * 3) & 7) == 6)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[6] << (x * 3));
n1->l = (n1->l) | (p[6] << (y * 3));
n1->l = (n1->l) ^ (p[6] << ((x + 8) * 3));
n1->l = (n1->l) ^ (p[6] << ((y + 8) * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((x + 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((x + 4) * 3));
n1->l = (n1->l) | (p[2] << (y * 3));
insertq(n, *n1, q);
}
if ((n.l >> ((y + 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((y + 4) * 3));
insertq(n, *n1, q);
}
}
if (pos[x] > 0)
{
if ((n.l >> ((x - 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << ((x + 1) * 3));
n1->l = (n1->l) ^ (p[2] << ((x - 1) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 1) * 3) & 7) == 7)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[7] << ((x - 1) * 3));
n1->l = (n1->l) ^ (p[5] << ((x - 2) * 3));
n1->l = (n1->l) | (p[5] << (x * 3));
n1->l = (n1->l) | (p[7] << (y * 3));
insertq(n, *n1, q);
}
}
if (pos[y] < 3)
{
if ((n.l >> ((y + 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((y + 1) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((y + 1) * 3) & 7) == 5)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[7] << ((y + 2) * 3));
n1->l = (n1->l) ^ (p[5] << ((y + 1) * 3));
n1->l = (n1->l) | (p[5] << (x * 3));
n1->l = (n1->l) | (p[7] << (y * 3));
insertq(n, *n1, q);
}
}
}
for (int i = 0; i < 2; i++)
{
if (pos1[x] > 0)
{
if ((n.l >> ((x - 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[2] << ((x - 4) * 3));
n1->l = (n1->l) | (p[2] << (x * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 4) * 3) & 7) == 3)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) ^ (p[1] << ((x - 8) * 3));
n1->l = (n1->l) ^ (p[2] << ((x - 4) * 3));
n1->l = (n1->l) | (p[3] << (x * 3));
insertq(n, *n1, q);
}
}
if (pos1[x] < 4)
{
if ((n.l >> ((x + 4) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((x + 4) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x + 4) * 3) & 7) == 1)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[1] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((x + 4) * 3));
n1->l = (n1->l) ^ (p[3] << ((x + 8) * 3));
insertq(n, *n1, q);
}
}
if (pos[x] > 0)
{
if ((n.l >> ((x - 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3)); n1->l = (n1->l) ^ (p[2] << ((x - 1) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x - 1) * 3) & 7) == 7)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[7] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((x - 1) * 3));
n1->l = (n1->l) ^ (p[5] << ((x - 2) * 3));
insertq(n, *n1, q);
}
}
if (pos[x] < 3)
{
if ((n.l >> ((x + 1) * 3) & 7) == 2)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[2] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((x + 1) * 3));
insertq(n, *n1, q);
}
else if ((n.l >> ((x + 1) * 3) & 7) == 5)
{
Node *n1 = new Node(n.l);
n1->l = (n1->l) | (p[5] << (x * 3));
n1->l = (n1->l) ^ (p[2] << ((x + 1) * 3));
n1->l = (n1->l) ^ (p[7] << ((x + 2) * 3));
insertq(n, *n1, q);
}
}
x = y;
}
}
至于镜像问题,则需要在原来的基础上改造即可
long long int Mirror(long long int l)
{
long long ll = 0;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 4; j++)
{
ll = ll | (((l >> ((i * 4 + j) * 3) & 7)) << (((4 * i) + 3 - j) * 3));
}
}
for (int i = 0; i < 20; i++)
{
if (((ll >> (i * 3)) & 7) == 7)
{
ll = ll ^ ((long long int)p[2] << (i * 3));
ll = ll ^ ((long long int)p[2] << ((i + 1) * 3)); i++;
}
}
return ll;
}
至于查重,我并没有使用自己写的哈希,而是继续使用了unordered_map,但是在定义时就要有所改变
unordered_map<long long int, int>hrd;
这样就可以使用同样的方法进行查重,代码就不再贴了,没有必要。
六.小结
总的来说,写华容道一定要理清思路,主要就使用了bfs的壳,还有一些哈希,将逻辑结构串清楚,写出来也不是难事了,我从一开始的迷茫到明白所有套路也不过两三周时间(是挺笨的,哈哈),所以关键是逻辑。实例是,在期末答辩之前用了三个小时帮人写了一边一维华容道,跑出来的时间大概在30ms,算比较慢的,后期的优化就是将一维棋盘转成long long int型,采用位运算进行移动,然后跑出来的时间为10ms,如果有时间,最好自己写一个队列,不要采用库里的队列,因为栈空间里的代码跑起来会比堆空间里的快上一些,系内还有一位大牛,自己写了一个很棒的哈希,达到了4ms,只能说打不过打不过这样子,哈哈哈。
我理解的华容道写法大概就是这样子,第一次写博客难免会有很多疏漏与不足之处,还请多多指教。