判断N数码问题是否有解

 http://acm.hdu.edu.cn/showproblem.php?pid=3600

题意:给定一个N数码的初始状态,问你是否可以到达终点状态

分析:先从8数码分析,把八数码的初始状态从上到下从左到右排成一排如 :

    2 1 3

    4 5 6  ----->2 1 3 4 5 6 7 8 #

    7 8 #

考虑空格与相邻位置的交换:两种情况,1)左右交换,2)上下交换。对于1)可知不会改变排列的逆序数,对于2)相当于在排列中向前或向后跳了两个数字

那么要么两个数字都比交换的那个数字大或小,这种情况逆序数加2或减2。.要么一个比它大,一个比它小,这种情况逆序数不变,所以综合这两种情况都不会

改变这个序列的逆序数。直观的看两个状态可互达当且仅当他们排列的逆序数同奇偶。

推广到N*N维的情况:

当N为奇数时:对于一个状态形成的序列,同样左右交换不会改变逆序数,上下交换逆序数加n-1或减n-1或不变因为n为基数所以逆序数的奇偶性不变

当N为偶数时:左右交换不改变逆序数,上下交换因为N为偶数,所以n-1为奇数所以上下交换一次奇偶性改变一次

结论:直观的看当N为奇数时奇偶同性可互达,N为偶数时,逆序数之和sum加上空格所在行距目标空格行的距离dis之和要和终点状态逆序数同奇偶

继续推广到N*N*N的情况:

左右交换,上下交换就不讨论了,这里只看层与层之间的交换,层与层的交换中间间隔了n^2-1个数,当n为奇数数时n^2-1为偶数,奇偶性不变,当n为偶数时为

交换一次,奇偶性改变一次,所以这种情况也需要考虑空格所在层与目标空格所在层之间的距离。

其中涉及到的证明不会,这只是个直观的结论,不过是正确的。证明参考:http://www.17shicheng.com/article/article.jsp?resourceID=29814

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用A*算法解决八数码问题的Java代码示例: ```java import java.util.*; public class EightPuzzle { private static final int[][] GOAL = {{1, 2, 3}, {4, 5, 6}, {7, 8, 0}}; // 目标状态 private static final int[][] DIRS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向 private static final int N = 3; // 棋盘大小 private static final int MAX_STATE = 1000000; // 最大状态数 private static int[][] start = new int[N][N]; // 起始状态 private static int[][] dist = new int[MAX_STATE][N]; // 到达每个状态的步数 private static int[][] pre = new int[MAX_STATE][N]; // 记录每个状态的前驱状态 private static int[][] vis = new int[MAX_STATE][N]; // 标记每个状态是否已经访问过 private static int[] pos = new int[9]; // 记录每个数字所在的位置 private static int getH(int[][] a) { // 计算估价函数h int res = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (a[i][j] == 0) continue; res += Math.abs(i - (a[i][j] - 1) / N) + Math.abs(j - (a[i][j] - 1) % N); } } return res; } private static int getId(int[][] a) { // 将状态转化为整数 int res = 0; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { res = res * 10 + a[i][j]; } } return res; } private static void printAns(int id) { // 输出解答 if (id == -1) return; printAns(pre[id][0]); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { System.out.print(start[i][j] + " "); } System.out.println(); } System.out.println(); } public static void main(String[] args) { Scanner sc = new Scanner(System.in); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { start[i][j] = sc.nextInt(); pos[start[i][j]] = i * N + j; } } int startId = getId(start); int goalId = getId(GOAL); if ((getH(start) & 1) != (getH(GOAL) & 1)) { // 判断是否有解 System.out.println("No solution!"); return; } int hh = 0, tt = 0; int[] q = new int[MAX_STATE]; Arrays.fill(dist, -1); Arrays.fill(vis, 0); dist[startId][0] = 0; vis[startId][0] = 1; q[0] = startId; while (hh <= tt) { int[][] cur = new int[N][N]; int t = q[hh++]; for (int i = N - 1; i >= 0; i--) { for (int j = N - 1; j >= 0; j--) { cur[i][j] = t % 10; t /= 10; } } if (getId(cur) == goalId) { // 到达目标状态 System.out.println("Steps: " + dist[goalId][0]); printAns(goalId); return; } int x = pos[0] / N, y = pos[0] % N; for (int i = 0; i < 4; i++) { // 扩展状态 int nx = x + DIRS[i][0], ny = y + DIRS[i][1]; if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue; int[][] nxt = new int[N][N]; for (int j = 0; j < N; j++) { for (int k = 0; k < N; k++) { nxt[j][k] = cur[j][k]; } } nxt[x][y] = nxt[nx][ny]; nxt[nx][ny] = 0; int nxtId = getId(nxt); if (vis[nxtId][0] == 0) { // 新状态 vis[nxtId][0] = 1; dist[nxtId][0] = dist[getId(cur)][0] + 1; pre[nxtId][0] = getId(cur); q[++tt] = nxtId; } } } System.out.println("No solution!"); // 无解 } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值