携程2017春招编程题——拼图
题目
解题思路
基础的解决算法是BFS队列解决,但是会超时。所以需要去重。
去重要考虑格子重复的情况,
9个格子,所以拼图有9!种状态,建立visited[9!]布尔数组记录访问情况,如何判断两种状态是否相同呢,通过康托展开。
哇,这个康托展开就坑爹了,谁tm学过这个呀,搜了一下才发现原来康拓展开就是通过一个数字来表示一个序列的排列(建立了排列关系 <–> 数字的对应关系)。
康托展开
使用示例来帮助理解利用Cantor展开如何求解本问题。
假如序列s=[“A”,”B”,”C”,”D”]
需要求序列s’=[“D”,”A”,”B”,”C”]是序列s的全排列中的第几个排列,记作X(s’);
X(s’) =
3(在序列DABC中有3个比D小)*3!+
0(在剩下的序列ABC中有0个比A小)*2!+
0(在剩下的序列BC中有0个比B小)*1!+
0(在剩下的序列C中有0个比C小)*0!
=18
代码实现
Todo: 写得比较凌乱,后续会整理
import java.util.*;
public class Main {
private static int[][] sample = { {1,2,3},{4,5,6},{7,8,0} };
private static final int MAX_NUM = 362880;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[][] matrix = new int[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix[i][j] = scanner.nextInt();
}
}
boolean visited[] = new boolean[MAX_NUM];
Arrays.fill(visited, false);
Strategy strategy = new Strategy(0, matrix, 0);
Queue<Strategy> queue = new LinkedList<>();
queue.offer(strategy);
while (!queue.isEmpty()) {
Strategy cur = queue.poll();
// 如果队列取出的访问过,则取下一个
if (visited[cantor(cur.matrix)]) {
continue;
}
// 如果得到结果,就返回
if (validate(cur.matrix)) {
System.out.println(cur.depth);
return;
}
// 队列取出的结果进行四个方向的变换
for (int i = -2; i <= 2; i++) {
if (0 - cur.orient == i) {
continue;
}
Position p = findZero(cur.matrix);
if (i == -2) {
// 0往右移动
int[][] m = copy(cur.matrix);
if (p.y < 2) {
int mid = m[p.x][p.y];
m[p.x][p.y] = m[p.x][p.y+1];
m[p.x][p.y+1] = mid;
Strategy s = new Strategy(i, m, cur.depth+1);
queue.offer(s);
}
continue;
}
if (i == -1) {
// 下移
int[][] m = copy(cur.matrix);
if (p.x < 2) {
int mid = m[p.x][p.y];
m[p.x][p.y] = m[p.x+1][p.y];
m[p.x+1][p.y] = mid;
Strategy s = new Strategy(i, m, cur.depth+1);
queue.offer(s);
}
continue;
}
if (i == 1) {
// 上移
int[][] m = copy(cur.matrix);
if (p.x > 0) {
int mid = m[p.x][p.y];
m[p.x][p.y] = m[p.x-1][p.y];
m[p.x-1][p.y] = mid;
Strategy s = new Strategy(i, m, cur.depth+1);
queue.offer(s);
}
continue;
}
if (i == 2) {
// 左移
int[][] m = copy(cur.matrix);
if (p.y > 0) {
int mid = m[p.x][p.y];
m[p.x][p.y] = m[p.x][p.y-1];
m[p.x][p.y-1] = mid;
Strategy s = new Strategy(i, m, cur.depth+1);
queue.offer(s);
}
continue;
}
}
// 四个方向变换玩,cur就玩完了,记录下来
visited[cantor(cur.matrix)] = true;
}
System.out.println(-1);
return;
}
private static int[][] copy(int[][] matrix) {
int res[][] = new int[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
res[i][j] = matrix[i][j];
}
}
return res;
}
private static boolean validate(int[][] matrix) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (sample[i][j] != matrix[i][j]) {
return false;
}
}
}
return true;
}
private static Position findZero(int[][] matrix) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (matrix[i][j] == 0) {
return new Position(i, j);
}
}
}
return null;
}
static class Strategy {
int orient; // 1 -上 -1-下 2-左 -2-右
int matrix[][];
int depth;
Strategy(int orient, int[][] matrix, int depth) {
this.orient = orient;
this.matrix = matrix;
this.depth = depth;
}
}
static class Position {
int x;
int y;
public Position(int x, int y) {
this.x = x;
this.y = y;
}
}
static int cantor(int[][] input)
{
int nums[] = new int[9];
for (int i = 0; i < 9; i++) {
nums[i] = input[i/3][i%3];
}
int fact[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
int ans = 0;
for (int i = 0; i < 9; ++i) {
int tmp = 0;
for (int j = i + 1; j < 9; ++j)
if (nums[j] < nums[i]) ++tmp;
ans += tmp * fact[9 - i - 1];
}
return ans;
}
}