题目描述:
卡片换位
你玩过华容道的游戏吗?
这是个类似的,但更简单的游戏。
看下面 3 x 2 的格子
+---+---+---+
| A | * | * |
+---+---+---+
| B | | * |
+---+---+---+
在其中放5张牌,其中A代表关羽,B代表张飞,* 代表士兵。还有一个格子是空着的。你可以把一张牌移动到相邻的空格中去(对角不算相邻)。游戏的目标是:关羽和张飞交换位置,其它的牌随便在哪里都可以。
输入格式:输入两行6个字符表示当前的局面
输出格式:
一个整数,表示最少多少步,才能把AB换位(其它牌位置随意)
例如,输入:
* A
**B
程序应该输出:
17
再例如,输入:
A B
***
程序应该输出:
12
思路:
这道题从字面意思是交换a和b,但是根据规则移动的条件只能移动空格周围的卡片。所以基本等价于移动空格,把各种情况遍历一遍找到一种a和b互换的情况。
由于空格只能移动到相邻位置,所以思路也就明确了可以用广度优先搜索。
空格可以移动有四个方位,上下左右。每移动一次就保存移动后的卡片排序,并且移动到该状态需要的步数是移动前的需要的步数加1.
终将有一次会找到一个卡片序列使得a和b相对起始序列是互换的。
简易代码版:
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
public class ExchangeAB {
public static void main(String[] args) {
String in = "A B***";
int aOrigin = in.indexOf("A");
int bOrgin = in.indexOf("B");
// 保存到某个局面需要的步数
HashMap save = new HashMap<>();
// 广度优先的队列
Queue queue = new LinkedList<>();
queue.add(in);
save.put(in, 0);
// 移动的方向上下左右
int[][] move = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
// 广度优先固定格式
while (!queue.isEmpty()) {
String nowStr = queue.poll();
int nowStep = save.get(nowStr);
// 如果A和B已经交换直接输出
if (nowStr.indexOf("B") == aOrigin && nowStr.indexOf("A") == bOrgin) {
System.out.println(nowStep);
return;
}
// 定位到空格的位置
int spacePos = nowStr.indexOf(" ");
int x = spacePos / 3;
int y = spacePos % 3;
char[] nowArr = nowStr.toCharArray();
// 向周围扩散
for (int[] arr : move) {
int tempx = x + arr[0];
int tempy = y + arr[1];
if (tempx >= 0 && tempx < 2 && tempy >= 0 && tempy < 3) {
// 移动空格位置
exchange(x, y, tempx, tempy, nowArr);
String tempStr = String.valueOf(nowArr);
if (save.get(tempStr) == null) {// 如果这个局面没来过则添加到队列
queue.add(tempStr);
save.put(tempStr, nowStep + 1);
}
// 复原空格位置方便给下个方向移动
exchange(x, y, tempx, tempy, nowArr);
}
}
}
}
public static void exchange(int x1, int y1, int x2, int y2, char[] arr) {
int pos1 = x1 * 3 + y1;
int pos2 = x2 * 3 + y2;
char temp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = temp;
}
}
详细代码版:
package real;
import java.awt.Point;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
public class CardMove {
//有四个移动方位
public static final int[][] move = { { 1, 0 }, { -1, 0 }, { 0, -1 },
{ 0, 1 } };
public static void main(String[] args) {
String a = "* A**B";//起始顺序(删除换行)
Queue queue = new LinkedList();
HashMap qmap = new HashMap();
//保存初始状态卡片A和B的位置
int posia = a.indexOf('A');
int posib = a.indexOf('B');
Point preblank = new Point();
Point blank = new Point();
//广度优先搜索起点是初始的卡片顺序
queue.add(a);
//保存初始状态顺序,到达该状态需要0步
qmap.put(a, 0);
String temp;
//广度优先搜索固定套路^_^一个循环
while (!queue.isEmpty()) {
//从队列中拿一个卡片序列出来
a = queue.poll();
temp = a;
//判断当前卡片顺序是否满足条件(a,b相对于初始顺序互换了)
if (a.charAt(posib) == 'A' && a.charAt(posia) == 'B') {
System.out.println(qmap.get(a));
break;
}
//找到空格所在位置方便进行移动遍历
int posiblank = a.indexOf(' ');
//计算出空格坐标
preblank.x = posiblank / 3;
preblank.y = posiblank % 3;
//进行移动遍历,空格有四个方向可以走上下左右
for (int[] arr : move) {
blank.x = preblank.x + arr[0];
blank.y = preblank.y + arr[1];
//判断移动后的目标位置是否合法(是不是出了矩阵)
if (blank.x >= 0 && blank.x <= 1 && blank.y >= 0
&& blank.y <= 2) {
//把字符串转字符数组,方便进行互换操作
char[] achar = a.toCharArray();
int tempindex = blank.x * 3 + blank.y;
//将空格移动到目标位置(就是将目标位置和空格交换)
char tempchar = achar[tempindex];
achar[tempindex] = achar[posiblank];
achar[posiblank] = tempchar;
//判断移动后的卡片序列是不是以前就走过了,走过了就不用再走了
if (qmap.get(String.valueOf(achar)) != null)
continue;
//把产生的新的序列保存下来,到的新的序列所需要的步数是老的序列的步数+1
qmap.put(new String(achar), qmap.get(temp) + 1);
//将新的序列加入到队列中,用于后面的搜索
queue.add(new String(achar));
}
}
}
}
}