算法设计与分析: 5-14 独立钻石跳棋问题

5-14 独立钻石跳棋问题


问题描述

独立钻石跳棋的棋盘上有 33 个方格,每个方格中可放 1 枚棋子。棋盘中最多可摆放 32枚棋子。下棋的规则是任一棋子可以沿水平或垂直方向跳过与其相邻的棋子进入空着的方格 并吃掉被跳过的棋子。试设计一个算法,对于任意给定的棋盘布局,找出一种下棋方法,使得最终棋盘上只剩下一个棋子。

独立钻石跳棋棋盘

对于给定的独立钻石跳棋的棋盘初始布局,和棋盘上最终剩下的棋子所在的位置(x,y), 编程计算一种遵循下棋的规则下棋方法,使最终棋盘上仅在位置(x,y)处有一枚棋子。当 (x,y)=(0,0)时,表示不指定棋子的最终位置。棋子位置的坐标定义如下。

独立钻石跳棋棋盘坐标

数据输入:
第一行中有 1 个正整数 n,表示棋盘的初始布局 中有 n 个棋子。第 2 行起每行 2 个数,分别是 n 个棋子的位置。最后 1 行是棋盘上最终剩下的棋子所在的位置。

结果输出:
每行有 2 对方格 坐标(a,b)和(c,d),表示从方格(a,b)跳到方格(c,d)。问题无解时输出“No”solution!。


Java

package Chapter5HuiSuFa;

import java.util.Scanner;

public class DuLiZuanShiTiaoQi {

    //Move: 棋子走步
    //(sx,xy):起步坐标 (xv,yv):走步方向
    private static class Move{
        int sx;
        int sy;
        int xv;
        int yv;
    }

    private static int MAX = 100000;
    private static int SIZE = 7;
    private static int num;
    private static int endx,endy;
    private static int[][] board = new int[SIZE][SIZE];//board[7][7]表示棋盘;board[x][y]:0->空方格 1->棋子占用方格 2->棋盘外方格

    private static Move[] moves = new Move[MAX];
    private static Move[] ans = new Move[MAX];
    private static int count;

    private static long[][] gmoves;
    private static long gboard;


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

        while (true){
            count = 0;

            int x,y;
            init();

            num = input.nextInt();
            for(int i=0; i<num; i++){
                x = input.nextInt();
                y = input.nextInt();
                board[x][y] = 1;
            }
            endx = input.nextInt();
            endy = input.nextInt();
            caches();
            genmv();
            initreg();
            if(backtrack(0))
                for(int i=0; i<num-1; i++)
                    out(ans[i]);
            else
                System.out.println("No Solution!");
        }
    }

    private static void init(){
        for(int i=0; i<SIZE; i++)
            for(int j=0; j<SIZE; j++)
                if((i<2||i>4) && (j<2||j>4))
                    board[i][j] = 2;
                else
                    board[i][j] = 0;
    }

    private static void caches(){
        for(int i=0; i<SIZE; i++)
            for(int j=0; j<SIZE; j++){
                if(i<6 && i>0){
                    cache(i,j,1,0);
                    cache(i,j,-1,0);
                }
                if(j<6 && j>0){
                    cache(i,j,0,1);
                    cache(i,j,0,-1);
                }
            }
    }

    //所有76个合法走步存储在数组moves中
    private static void cache(int x, int y, int xv, int yv){
        if((board[x][y]!=2) && (board[x+xv][y+yv])!=2 && (board[x-xv][y-yv]!=2)){
            Move m = new Move();
            m.sx = x-xv;
            m.sy = y-yv;
            m.xv = xv;
            m.yv = yv;
            moves[count++] = m;
        }
    }

    //gmoves存储相应的走步状态,用于判定走步的合法性,由数组moves产生gmoves的值
    private static void genmv(){
        gmoves = new long[count][2];
        for(int j=0; j<count; j++){
            Move m;
            m = moves[j];
            gmoves[j][0] = coord2bit(m.sx,m.sy);
            gmoves[j][0] |= coord2bit(m.sx+m.xv,m.sy+m.yv);
            gmoves[j][0] |= coord2bit(m.sx+2*m.xv,m.sy+2*m.yv);
            gmoves[j][1] = coord2bit(m.sx,m.sy)|coord2bit(m.sx+m.xv,m.sy+m.yv);
        }
    }

    //棋盘上棋子的一种状态可以看成全部方格(33个)的一种二进制排列
    //有棋子为1,无棋子为0
    //将棋盘坐标转换为相应的位
    //注意64位long型
    private static long coord2bit(int x, int y){
        int pos;
        switch (y){
            case 0: pos = x-2; break;
            case 1: pos = x+1; break;
            case 5: pos = x+25; break;
            case 6: pos = x+28; break;
            default: pos = x-8+y*7;
        }

        return (long)1<<pos;//左移
    }

    //gboard: 表示当前棋盘整体状态,由initreg进行初始化
    private static void initreg(){
        gboard = 0;
        for(int i=0; i<SIZE; i++)
            for(int j=0; j<SIZE; j++)
                if(board[i][j] == 1)
                    gboard |= coord2bit(i,j);
    }

    private static boolean backtrack(int t){
        long[] m;
        if(finish(t)) return true;
        for(int i=0; i<count; i++){
            m = gmoves[i];
            if(ok(m)){
                next(m);
                if(backtrack(t+1)){
                    ans[t] = find(m);
                    return true;
                }
                restore(m);
            }
        }

        return false;
    }

    //检测终止条件
    //(endx,endy): 终止方格坐标
    //num: 初始棋盘上棋子个数
    private static boolean finish(int t){
        if(endx>0 || endy>0)
            return gboard==coord2bit(endx,endy);
        else
            return t>num-2;
    }

    //判定走步move的合法性
    private static boolean ok(long[] move){
        return ((gboard & move[0]) == move[1]);
    }

    //将棋盘状态变换为走步move后的状态
    private static void next(long[] move){
        gboard ^= move[0];
    }

    //将棋盘状态恢复为走步move前的状态
    private static void restore(long[] move){
        gboard ^= move[0];
    }

    //找出与走步状态相应的走步描述
    private static Move find(long[] m){
        for(int i=0; i<count; i++)
            if(gmoves[i] == m)
                return moves[i];

        return new Move();
    }

    private static void out(Move m){
        System.out.println("("+m.sx+","+m.sy+")("+(m.sx+2*m.xv)+","+(m.sy+2*m.yv)+")");
    }
}

Input & Output

8
4 1
5 2
5 3
6 3
3 4
4 4
4 5
4 6
0 0
(3,4)(5,4)
(4,6)(4,4)
(4,4)(6,4)
(6,4)(6,2)
(6,2)(4,2)
(4,1)(4,3)
(5,3)(3,3)


9
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
2 4
3 4
(2,3)(0,3)
(2,1)(2,3)
(3,3)(1,3)
(0,3)(2,3)
(2,4)(2,2)
(3,1)(3,3)
(1,2)(3,2)
(3,2)(3,4)

Reference

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

猜你喜欢

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