poj1198:
Solitaire
Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered from 1 to 8, from the top to the bottom and from left to right respectively.
There are four identical pieces on the board. In one move it is allowed to:
-
- move a piece to an empty neighboring field (up, down, left or right),
- jump over one neighboring piece to an empty field (up, down, left or right).
There are 4 moves allowed for each piece in the configuration shown above. As an example let's consider a piece placed in the row 4, column 4. It can be moved one row up, two rows down, one column left or two columns right.
Write a program that:
- reads two chessboard configurations from the standard input,
- verifies whether the second one is reachable from the first one in at most 8 moves,
- writes the result to the standard output.
用广度优先搜索求最小路径(其实原题给定了最大步数,深搜搜到第八步跳出循环也可以解决,但是容易超时)。
根据广度优先搜索的几个基本步骤,在写题之前先把思路整理好:
- 每个结点的棋盘都将只有四个点是棋子,也就是说,与其用8x8 的数组存储,不如只存坐标。
- 每个父结点的每个棋子都有4种走法,四个棋子有16种走法。如果搜到8步,那将是168,也就是232=429496729,数组开不下,并且必定会超时。
解决的办法有两个:
- 判重并且使用到哈希解码,对于冲突,我们可以用字符串记录路径,发生冲突时叠加字符串即可。对于判重,从头到尾的比较肯定会超时,我们可以开一个86878889长度的数组(可能出现的最大项,可以用平移优化),用来打标记判重。
- 我们可以写一个双向BFS(个人更喜欢这种做法),双向搜索只需要搜4层就够了。
值得注意的是,如果开数组打标记的方法判重,一定要把每个结点的四个坐标都排好序!
以下是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 //定义棋子 7 struct point{ 8 int x, y; 9 }; 10 //定义初始、目标棋盘 11 point a[5], b[5]; 12 int numa, numb; 13 //输出棋盘 14 void print(point a[5]){ 15 16 for (int i=1; i<=4; ++i) printf("%d%d", a[i].x, a[i].y); 17 printf("\n"); 18 19 } 20 21 //将棋盘转换为十进制数 22 int f(point a[5]){ 23 int s=0; 24 for (int i=1; i<=4; ++i) s=s*100+a[i].x*10+a[i].y; 25 return s; 26 } 27 bool cmp(point a, point b){ 28 return a.x<b.x or (a.x==b.x and a.y<b.y); 29 } 30 31 //偏移量 32 point d[5]={{ 0, 0}, 33 {+1, 0}, 34 {-1, 0}, 35 { 0,+1}, 36 { 0,-1}}; 37 38 //定义判重的数组 flag 39 bool flag[85868789]; 40 //定义节点 41 struct node{ 42 point a[5]; 43 int dep; 44 int fa; 45 }; 46 node data[6000000]; 47 //判断x,y位置是否出界 48 bool check(int x, int y){ 49 return (x>=1 and x<=8 and y>=1 and y<=8); 50 } 51 //检查a棋盘的x,y位置是否有棋子 有返回真 52 bool check1(point a[5], int x, int y){ 53 for (int i=1; i<=4; ++i) 54 if (a[i].x==x and a[i].y==y) return true; 55 return false; 56 } 57 //复制棋盘 58 void copy1(point a[5], point b[5]){ 59 for (int i=1; i<=4; ++i) b[i].x=a[i].x,b[i].y=a[i].y; 60 } 61 int op , cl; 62 void print1(int cl){ 63 64 int ans[10], n=0; 65 while (cl!=-1){ 66 ans[++n]=cl; 67 cl=data[cl].fa; 68 } 69 printf("\n\n"); 70 for (int i=n; i>1; --i) print(data[ans[i]].a); 71 print(data[ans[1]].a); 72 } 73 int main(){ 74 //freopen("poj1198.in", "r", stdin); 75 for (int i=1; i<=4; ++i) scanf("%d%d", &a[i].x, &a[i].y); 76 for (int i=1; i<=4; ++i) scanf("%d%d", &b[i].x, &b[i].y); 77 //print(a); 78 //print(b); 79 sort(a+1, a+5, cmp); 80 sort(b+1, b+5, cmp); 81 numa=f(a); 82 numb=f(b); 83 //print(a); 84 //print(b); 85 //printf("%d %d\n", numa, numb); 86 //特判初始状态和目标状态 87 if (numa==numb){ 88 89 printf("YES"); 90 return 0; 91 92 } 93 //初始化 首节点入队 首尾设置指针 94 copy1(a, data[1].a); data[1].dep=0; data[1].fa=-1; 95 //memset(flag, false, sizeof flag); 96 flag[numa]=true; 97 98 op = 0, cl = 1; 99 bool f1=false; 100 while (op<cl and !f1 and data[op+1].dep<8){ 101 ++op; 102 103 int dep=data[op].dep; 104 105 for (int i=1; i<=4; ++i){ 106 107 for (int j=1; j<=4; ++j){ 108 109 copy1(data[op].a, a); //每个操作要从原始棋盘开始 110 111 //x1,y1移动一步的位置 112 //x2,y2跳跃一步的位置 113 int x1=a[i].x+d[j].x, y1=a[i].y+d[j].y; 114 int x2=x1+d[j].x, y2=y1+d[j].y; 115 116 if (check(x1, y1) and !check1(a, x1, y1)) {//x1,y1位置不出界且x1,y1位置无棋子 117 118 a[i].x=x1; a[i].y=y1; //移动到新位置 119 sort(a+1, a+5, cmp); //排序 120 numa=f(a); //转化为数 121 if (!flag[numa]){ //判重 122 flag[numa]=true; //标记 123 124 ++cl; //入队 125 copy1(a, data[cl].a); 126 data[cl].dep=dep+1; 127 data[cl].fa=op; 128 129 //判断目标 130 if (numa==numb){ 131 f1=true; 132 break; 133 } 134 } 135 }else 136 if (check(x1, y1) and check(x2, y2) and 137 check1(a, x1, y1) and !check1(a, x2, y2)) { 138 //x1,y1,x2,y2位置不出界且x1,y1位置有棋子,x2,y2位置无棋子 139 140 a[i].x=x2; a[i].y=y2; //跳跃到新位置 141 sort(a+1, a+5, cmp); //排序 142 numa=f(a); //转化为数 143 if (!flag[numa]){ //判重 144 flag[numa]=true; //标记 145 146 ++cl; //入队 147 copy1(a, data[cl].a); 148 data[cl].dep=dep+1; 149 data[cl].fa=op; 150 151 //判断目标 152 if (numa==numb){ 153 f1=true; 154 break; 155 } 156 } 157 } 158 } 159 if (f1) break; 160 } 161 } 162 if (f1) printf("YES\n"); else printf("NO\n"); 163 return 0; 164 }