在一个2^k * 2^k的方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
在棋盘覆盖问题中,要用到下图所示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。易知在任何一个2^k * 2^k的棋盘覆盖中,用到的L型骨牌个数恰好为(4^k -1)/3。
图一 k=2时的一个特殊棋盘 图二 4种不同形态的L型骨牌
用分治策略,可以设计出接棋盘覆盖问题的简洁算法。
当k>0是,将2^k * 2^k棋盘分割为4个2^(k-1) 2^(k-1)子棋盘,特殊方格必位于4个较小子棋盘之一中,其余三个子棋盘中没有特殊方格。为了将这3个没有特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的汇合处,这3个子棋盘被L型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将原问题转化为4个较小规模的期盼覆盖问题。递归使用这种分割,直至棋盘简化为1*1棋盘。
具体实现如下:
ChessBoard.java
import javax.swing.*; public class ChessBoard extends JFrame { public static void main(String[] args) { JFrame f=new ChessBoard(); f.add(new ChessPanel(8)); f.setTitle("棋盘覆盖算法"); f.setSize(600,600); f.setLocationRelativeTo(null);//center f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
ChessPanel.java
import java.util.*; import javax.swing.*; import java.awt.*; class ChessPanel extends JPanel { private int num = 2;//棋盘数量,默认为2*2( (2^k)*(2^k) ) private JLabel board[][];//棋牌数组 private int dx=0,dy=0;//特殊方格的位置,默认为(0,0) private Color[] lp_type = {Color.blue,Color.gray,Color.green,Color.yellow,Color.red,Color.cyan,Color.magenta};//定义L骨牌的颜色 private int tile = 0;//不同颜色L型骨牌的编号 //通过该构造函数可以预置棋盘规格(2^k)*(2^k) 其中num = 2^k public ChessPanel(int num) { this.num=num;//num = 2^k //棋盘初始化 this.setLayout(new GridLayout(num,num)); this.addLabels(); //特殊方格初始化 Random ran = new Random();//随机产生 dx = ran.nextInt(num);//特殊方格的行 dy = ran.nextInt(num);//特殊方格的列 board[dx][dy].setBackground(Color.black);//特殊方格 使用黑色表示 chessBoard(0,0,dx,dy,num);//分治策略的算法,覆盖棋盘 } //棋盘初始化 public void addLabels() { board = new JLabel[num][num]; for(int i=0;i<num;i++) { for (int j=0;j<num;j++) { board[i][j] = new JLabel(i+","+j); board[i][j].setForeground(Color.white); board[i][j].setBorder(BorderFactory.createLineBorder(Color.black)); board[i][j].setOpaque(true); this.add(board[i][j]); } } } /* * @function 分治策略的算法 * @parm tr 棋盘左上角方格的行号 * tc 棋盘左上角方格的列号 * dr 特殊方格所在的行号 * dc 特殊方格所在的列号 * size 2^k,棋盘的规格(2^k)*(2^k) */ public void chessBoard(int tr,int tc,int dr,int dc,int size) { if(size == 1) return;//退出递归 int s = size/2; //分割棋盘 int t = (tile++)%lp_type.length;//不同颜色L型骨牌的编号,构成循环 //覆盖左上角子棋盘 if(dr<tr+s&&dc<tc+s) //特殊方格在此棋盘中 chessBoard(tr,tc,dr,dc,s); else//此棋盘中无特殊方格 { //用t号L型骨牌覆盖右下角 board[tr+s-1][tc+s-1].setBackground(lp_type[t]); //覆盖其余方格 chessBoard(tr,tc,tr+s-1,tc+s-1,s); } //覆盖右上角子棋盘 if(dr<tr+s&&dc>=tc+s) //特殊方格在此棋盘中 chessBoard(tr,tc+s,dr,dc,s); else//此棋盘中无特殊方格 { //用t号L型骨牌覆盖左下角 board[tr+s-1][tc+s].setBackground(lp_type[t]); //覆盖其余方格 chessBoard(tr,tc+s,tr+s-1,tc+s,s); } //覆盖左下角子棋盘 if(dr>=tr+s&&dc<tc+s) //特殊方格在此棋盘中 chessBoard(tr+s,tc,dr,dc,s); else//此棋盘中无特殊方格 { //用t号L型骨牌覆盖右上角 board[tr+s][tc+s-1].setBackground(lp_type[t]); //覆盖其余方格 chessBoard(tr+s,tc,tr+s,tc+s-1,s); } //覆盖右下角子棋盘 if(dr>=tr+s&&dc>=tc+s) //特殊方格在此棋盘中 chessBoard(tr+s,tc+s,dr,dc,s); else//此棋盘中无特殊方格 { //用t号L型骨牌覆盖左上角 board[tr+s][tc+s].setBackground(lp_type[t]); //覆盖其余方格 chessBoard(tr+s,tc+s,tr+s,tc+s,s); } } }
运行结果如下: