算法设计与分析: 6-23 重排n²宫问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/IOIO_/article/details/81207721

6-23 重排n²宫问题


问题描述

重排九宫是一个古老的单人智力游戏。据说重排九宫起源于我国古时由三国演义故事“关羽义释曹操”而设计的智力玩具“华容道”,后来流传到欧洲,将人物变成数字。原始 的重排九宫问题是这样的:将数字 1~8 按照任意次序排在 3 × 3 的方格阵列中,留下一个空格。与空格相邻的数字,允许从上,下,左,右方向移动到空格中。游戏的最终目标是通过 合法移动,将数字 1~8 按行排好序。在一般情况下,重排 n2 宫问题是将数字 1~ n 2 1 按照 任意次序排在 n × n 的方格阵列中,留下一个空格。允许与空格相邻的数字从上,下,左,右 4 个方向移动到空格中。游戏的最终目标是通过合法移动,将初始状态变换到目标状态。

重排n²宫

对于给定的 n × n 方格阵列中数字 1~ n 2 1 初始排列和目标状态,编程计算将初始排列通 过合法移动变换为目标状态最少移动次数。

数据输入:
第 1 行有 1 个正整数 n。以下的 n 行是 n × n 方格 阵列的中数字 1~ n 2 1 的初始排列,每行有 n 个数字表示该行方格中的数字, 0 表示空格。 接着的 n 行是方格阵列中数字 1~ n 2 1 的目标状态。


Java

package Chapter6FenZhiXianJieFa;

import java.util.Collections;
import java.util.Scanner;
import java.util.Vector;

public class ChongPaiNxNGong {

    private static class Board{
        int y,x;               //空格位置
        Vector<Integer> path;
        int dist;              //manhattan距离
        Vector<Integer> boardm;

        private int heur(){
            return dist;
        }

        private void getboard(int[] m){
            for(int i=0; i<boardsz; i++){
                boardm.add(m[i]);
                if(m[i] == 0) {y=i/rowsz; x=i%rowsz;}
            }
            dist=0;
            for(int i=0; i<boardsz; i++)
                if(boardm.get(i) != 0) dist+=getdist(boardm.get(i),i);
        }

        //按3个方向移动
        private boolean move(int dir){
            final int[][] step = {{0,-1},{0,1},{-1,0},{1,0}};//空格移动方向: 上 下 左 右//dir: 0 1 2 3
            int nx = x+step[dir][0];
            int ny = y+step[dir][1];
            if((path.isEmpty()||op[dir]!=(path.lastElement())) && nx>-1 && nx<rowsz && ny>-1 && ny<rowsz){
                dist = dist+1-getdist(boardm.get(ny*rowsz+nx),ny*rowsz+nx)+getdist(boardm.get(ny*rowsz+nx),y*rowsz+x);
                Collections.swap(boardm,y*rowsz+x,ny*rowsz+nx);
                y=ny; x=nx;
                path.add(dir);
                return true;
            }else
                return false;
        }

        //计算manhattan距离
        private int getdist(int v, int loc){
            int dis = Math.abs((pos[v]%rowsz)-(loc%rowsz));
            dis += Math.abs((pos[v]/rowsz)-(loc/rowsz));

            return dis;
        }

        //到达目标状态
        private boolean reached(){
            for(int i=0; i<boardsz; i++)
                if(boardm.get(i) != dest[i]) return false;

            return true;
        }

        private void out(){
            char[] dir = {'U','D','L','R'};//空格移动方向: 上 下 左 右//dir: 0 1 2 3
            System.out.println(path.size());
            for(int i=0; i<path.size(); i++){
                System.out.print(dir[path.get(i)]);
                if(i%20 == 19) System.out.println();
            }
            if(path.size()%20 != 0) System.out.println();
        }
    }

    private static final int[] op = {1,0,3,2};
    private static int maxdep;
    private static int rowsz,boardsz;
    private static int[] sour,dest,pos;

    public static void main(String[] args){
        Scanner input = new Scanner(System.in);

        while (true){
            init(input);

            if(odd()) idastar();
            else System.out.println("No Solution!");
        }
    }

    private static void init(Scanner input){
        rowsz = input.nextInt();

        boardsz = rowsz*rowsz;
        sour = new int[boardsz];
        dest = new int[boardsz];
        pos = new int[boardsz];

        for(int i=0; i<boardsz; i++)
            sour[i] = input.nextInt();

        for(int i=0; i<boardsz; i++){
            dest[i] = input.nextInt();
            pos[dest[i]] = i;
        }
    }

    private static boolean odd(){
        int[] c = new int[boardsz];
        int count=0,count1=0,i1=0,j1=0,i2=0,j2=0;
        for(int k=0; k<boardsz; k++){
            c[dest[k]] = sour[k];
            if(sour[k] == 0) {i1=k/rowsz+1; j1=k%rowsz+1;}
            if(dest[k] == 0) {i2=k/rowsz+1; j2=k%rowsz+1;}
        }
        int posi = ((i1+i2)%2+(j1+j2)%2)%2;
        for(int j=0; j<boardsz; j++){
            int k=c[j],k1=j;
            count1 = 0;
            while (k >= 0) {k=c[k1]; c[k1]=-1; k1=k; count1++;}
            if(count1 > 0) count+=count1-2;
        }
        if(count%2 == posi) return true;
        else return false;
    }

    private static boolean solve(int dep, Board E){
        if(dep+E.dist <= maxdep){
            if(E.reached()) {E.out(); return true;}
            for(int i=0; i<4; i++){
//                Board N = E;
                Board N = new Board();
                N.x = E.x;
                N.y = E.y;
                N.dist = E.dist;
                N.boardm = new Vector<>();
                N.path = new Vector<>();

                N.boardm = (Vector)E.boardm.clone();
                N.path = (Vector)E.path.clone();

                if(N.move(i))
                    if(solve(dep+1,N))
                        return true;
            }
        }

        return false;
    }

    //IDA*算法
    private static void idastar(){
        Board E = new Board();
        E.boardm = new Vector<>();
        E.path = new Vector<>();
        E.getboard(sour);
        maxdep = E.heur();
        if(maxdep == 0){
            System.out.println(0);
            return;
        }
        while (!solve(0,E))
            maxdep += 2;
    }
}

Input & Output

3
1 2 3
4 0 6
7 5 8
1 2 3
4 5 6
7 8 0
2
DR


3
1 2 3
4 0 6
7 5 8
1 2 3
4 5 6
7 8 0
2
DR


3
6 7 3 
2 5 1 
8 4 0 
1 2 3 
4 5 6 
7 8 0 
26
ULULDRURDDLURDLLURRU
LLDRRD


3
7 2 8 
5 4 3 
6 0 1 
1 2 3 
4 5 6 
7 8 0 
No Solution!


3
7 0 3 
8 1 2 
6 5 4 
1 2 3 
4 5 6 
7 8 0 
17
DRDLLUURDRDLLURRD

Reference

王晓东《计算机算法设计与分析》

猜你喜欢

转载自blog.csdn.net/IOIO_/article/details/81207721