【问题标题】:divide and conquer algorithm分治算法
【发布时间】:2013-02-12 17:58:16
【问题描述】:

我得到了一个 2^k * 2^k 大小的棋盘,其中一个棋子被随机移除,使其成为有缺陷的棋盘。任务是用“trominos”填充,这是一个由 3 个瓷砖组成的 L 形图形。

解决它的过程并不太难。如果棋盘是 2x2,那么只需要一个 tromino 就可以填满它。对于任何更大的尺寸,它必须分成四等份(制作四块 2^(k-1) 大小的木板),在中心点放置一个 tromino,因此每个象限都有一个填充瓷砖。之后,可以递归地填充棋盘,直到每块棋子都被随机颜色的tromino填充。

我的主要问题实际上是实现代码。我的 Java 编程技能通常很薄弱,而且我常常很难找到一个开始的地方。唯一要做的工作是在 tiling 类中的 tile 方法中,该方法将要平铺的缺陷板、开始平铺的行和列以及要填充的瓷砖数量作为输入。这是一个家庭作业问题,所以我只是在寻找一些指导或一个开始的地方 - 任何帮助将不胜感激。

public class BoardViewer extends JFrame {

private static final int WIDTH = 1024;
private static final int HEIGHT = 768;
private static final int OFFSET = 30;

public static final int UPVERT = 1;
public static final int DNVERT = 2;
public static final int RHORIZ = 4;
public static final int LHORIZ = 8;
public static final int FILL = 16;

private Color [][] board;

public BoardViewer(DeficientBoard db) {
    super();
    setSize(WIDTH + (OFFSET*2), HEIGHT + (OFFSET*2));
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setResizable(false);

    board = db.getBoardColor();
}

public void paint(Graphics g) {
    super.paint(g);

    int width = WIDTH/board[0].length;
    int height = HEIGHT/board.length;

    for (int r = 0; r < board.length; r++)
        for (int c = 0; c < board[r].length; c++) {
            g.setColor(board[r][c]);

            int x = c*width + OFFSET;
            int y = r*height + OFFSET + (OFFSET/2);

            g.fillRect(x+1, y+1, width-1, height-1);
        }
}

}

public class DeficientBoard {

private int n;
private Color board[][];

// The four rotations of the tile.
// UL is XX
//       X
// UR is XX
//        X
// LL is X
//       XX
// LR is  X
//       XX
public final int UL = 0;
public final int UR = 1;
public final int LL = 2;
public final int LR = 3;

/**
 * Create a 2^k x 2^k deficient board.
 * 
 * @param k power
 */
public DeficientBoard(int k) {
    n = (int)Math.pow(2, k);
    createBoard(Color.LIGHT_GRAY);
}

/**
 * Actually create an n x n deficient board.
 * 
 * @param color background color
 */
private void createBoard(Color color) {
    board = new Color[n][n];
    for (int r = 0; r < board.length; r++)
        for (int c = 0; c < board[0].length; c++)
            board[r][c] = color;

    int d_row = (int)(Math.random() * n);
    int d_col = (int)(Math.random() * n);
    board[d_row][d_col] = Color.BLACK;
}

/**
 * Given a row and column and shape based on that point
 * place a tromino of the given color.
 * 
 * @param r row
 * @param c column
 * @param s shape (UL, UR, LL, LR)
 * @param theColor a Color
 */
public void placeTromino(int r, int c, int s, Color theColor) {
    if (s == UL) {
        board[r][c] = theColor; 
        board[r][c+1] = theColor;
        board[r+1][c] = theColor;
    } else if (s == UR) {
        board[r][c] = theColor;
        board[r][c+1] = theColor;
        board[r+1][c+1] = theColor;
    } else if (s == LL) {
        board[r][c] = theColor;
        board[r+1][c] = theColor;
        board[r+1][c+1] = theColor;
    } else {
        board[r+1][c] = theColor;
        board[r+1][c+1] = theColor;
        board[r][c+1] = theColor;
    }
}

/**
 * Get the 2^k x 2^k board.
 * 
 * @return the Color board.
 */
public Color[][] getBoardColor() {
    return board;
}

/**
 * Find and return the deficient row.
 * 
 * @param row row
 * @param col column
 * @param sz size of the baord
 * @return the row the deficient block is located
 */
public int getDeficientRow(int row, int col, int sz) {

    for (int r = row; r < (row + sz); r++)
        for (int c = col; c < (col + sz); c++)
            if (board[r][c] != Color.LIGHT_GRAY)
                return r;

    return -1;
}

/**
 * Find and return the deficient column.
 * 
 * @param row row
 * @param col column
 * @param sz size of the baord
 * @return the row the deficient block is located
 */
public int getDeficientCol(int row, int col, int sz) {
    for (int r = row; r < (row + sz); r++)
        for (int c = col; c < (col + sz); c++)
            if (board[r][c] != Color.LIGHT_GRAY)
                return c;

    return -1;
}

/**
 * Get the size of the deficient board.
 * 
 * @return the size
 */
public int getSize() {
    return n;
}

/**
 * Display information about the deficient board.
 */
public String toString() {
    return ("Deficient board of size " 
             + n + "x" + n
             + " with position missing at (" 
             + getDeficientRow(0, 0, n) + "," + getDeficientCol(0, 0, n) +").");
}

}

public class Tiling {

private static Color randColor() {
    int r = (int)(Math.random() * 256);
    int g = (int)(Math.random() * 256);
    int b = (int)(Math.random() * 256);

    return new Color(r, g, b);
}

public static void tile(DeficientBoard db, int row, int col, int n) {


}

public static void main(String[] args) {

    DeficientBoard db = new DeficientBoard(3);
    System.out.println(db);

    tile(db, 0, 0, db.getSize());

    BoardViewer bv = new BoardViewer(db);
    bv.setVisible(true);

}

}

【问题讨论】:

    标签: java recursion divide-and-conquer


    【解决方案1】:

    一般来说,当递归函数实现分治算法时,它必须处理两种基本情况:

    • 基本情况。这就是你完成分裂的情况,需要征服一点。在您的作业中,基本情况是 n = 2 的情况,在这种情况下,您只需要找出四个瓷砖中的哪一个缺失/绘制(使用 DefectiveBoard.getDeficientRowDefectiveBoard.getDeficientCol ) 并添加适当的 triomino 以覆盖其他三个瓷砖。
    • 递归案例。这是您没有完成划分的情况,因此您需要划分(即递归),并且(取决于算法)可能需要在之前或之后进行一些征服递归。在您的作业中,递归情况是 n > 2 的情况。在这种情况下,您需要做两件事:
      • 找出四个象限中的哪一个有缺失/上漆的瓷砖,并添加适当的三格板以覆盖其他三个象限中的每一个的一个瓷砖。
      • 递归,称自己四次(每个象限一次)。

    一个好的起点是写“这是基本情况吗?”检查,并实施基本情况。

    之后,如果您不知道如何编写递归案例,一种方法是暂时编写一个“基础之上的”案例(n = 4),看看你是否可以概括它。如果没有,那么您可能暂时编写一个“比基础高二”的情况(n = 8),依此类推。 (一旦你的递归算法开始工作,你就可以删除这些特殊情况,因为它们完全被一般递归情况所覆盖。)

    【讨论】:

      【解决方案2】:

      嗯,这是一个更难解决的问题。但是,鉴于您编写了多少代码,我会说您具有技能,因此我不会对此有任何自觉。

      我没有制定完整的解决方案,但我认为如果您从移除的瓷砖开始并在其两侧放置一个 trominos。然后继续将 trominos 放在最后一个 trominos 的两侧。您正在“舀”您上次放在板上的 tromino。一旦你这样做到板的边缘。剩下的就是tromino 形状的位置。这是我的意思的一个例子(X是掉落的瓷砖,即间隙,Y是trominos):

       _ _ _ _
      |_|_|_|_|
      |_|Y|Y|_|
      |_|Y|X|Y|
      |_|_|Y|Y|
      
       _ _ _ _
      |Y|Y|_|_|
      |Y|Y|Y|_|
      |_|Y|X|Y|
      |_|_|Y|Y|
      

      一旦棋盘的边缘被填满,您就可以开始在棋盘的其余部分投下炸弹,就像炸弹一样。我觉得这里有一种模式,你可以在填充对角线的同时填充第二部分,这是可重复的。但是,如果您找不到,则创建一个递归例程,将间隙勺到边缘,然后过渡到以对角线模式添加 trominos。提示您必须仅在第一个堆栈帧中进行转换。

      祝你好运。

      【讨论】:

      • -1,对不起。您提出的算法远不如 OP 在问题第二段中描述的算法。我不明白你为什么要建议它,除非你完全误解了 OP 的问题。回复:“考虑到你写了多少代码,我会说你有技能”:如果我正确理解了这个问题,(s)他实际上还没有写任何代码;相反,发布的代码是作为作业问题的一部分提供的,实际任务是实现Tiling.tile
      • 好吧,我确实说过我没有完整的解决方案,我重新阅读了这篇文章,并没有提到谁编写了代码。 OP 描述的算法没有足够的细节来决定如何填充 trominos,或者您必须将所有板递归地划分为 4x4 并解决这些问题,而不是 2x2。所以我认为“非常低劣”有点过于戏剧化了,因为它没有得到充分的描述。
      猜你喜欢
      • 2021-05-06
      • 2012-10-28
      • 2017-04-07
      • 1970-01-01
      • 2013-02-02
      • 2014-10-28
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多