Sudoku 数独 Dancing Links模板
List<int[][]> solutions = dlx.getSolutions();
https://code.google.com/p/sudoku-dlx/source/checkout
DLX.java
import java.util.ArrayList; import java.util.List; public class DLX { private static final int ROW = 4096 + 50; private static final int COL = 1024 + 50; private static final int N = 4 * 9 * 9; private static final int m = 3; DLXNode row[] = new DLXNode[ROW]; DLXNode col[] = new DLXNode[COL]; DLXNode head; private int n; private int num = 2; private int size[] = new int[COL]; int data[][] = new int[9][9]; List<int[][]> solutions; public DLX(int r, int c) { n = m * m; head = new DLXNode(r, c); head.U = head.D = head.L = head.R = head; for (int i = 0; i < c; ++i) { col[i] = new DLXNode(r, i); col[i].L = head; col[i].R = head.R; col[i].L.R = col[i].R.L = col[i]; col[i].U = col[i].D = col[i]; size[i] = 0; } for (int i = r - 1; i > -1; --i) { row[i] = new DLXNode(i, c); row[i].U = head; row[i].D = head.D; row[i].U.D = row[i].D.U = row[i]; row[i].L = row[i].R = row[i]; } } public void addNode(int r, int c) { DLXNode p = new DLXNode(r, c); p.R = row[r]; p.L = row[r].L; p.L.R = p.R.L = p; p.U = col[c]; p.D = col[c].D; p.U.D = p.D.U = p; ++size[c]; } public void addNode(int i, int j, int k) { int r = (i * n + j) * n + k; addNode(r, i * n + k - 1); addNode(r, n * n + j * n + k - 1); addNode(r, 2 * n * n + block(i, j) * n + k - 1); addNode(r, 3 * n * n + i * n + j); } int block(int x, int y) { return x / m * m + y / m; } public void cover(int c) { if (c == N) return; col[c].delLR(); DLXNode R, C; for (C = col[c].D; C != col[c]; C = C.D) { if (C.c == N) continue; for (R = C.L; R != C; R = R.L) { if (R.c == N) continue; --size[R.c]; R.delUD(); } C.delLR(); } } public void resume(int c) { if (c == N) return; DLXNode R, C; for (C = col[c].U; C != col[c]; C = C.U) { if (C.c == N) continue; C.resumeLR(); for (R = C.R; R != C; R = R.R) { if (R.c == N) continue; ++size[R.c]; R.resumeUD(); } } col[c].resumeLR(); } public boolean solve(int depth) { if (head.L == head) { int solution[][] = new int[n][n]; for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) solution[i][j] = data[i][j]; solutions.add(solution); if (solutions.size() == num) return true; return false; } int minSize = 1 << 30; int c = -1; DLXNode p; for (p = head.L; p != head; p = p.L) if (size[p.c] < minSize) { minSize = size[p.c]; c = p.c; } cover(c); for (p = col[c].D; p != col[c]; p = p.D) { DLXNode cell; p.R.L = p; for (cell = p.L; cell != p; cell = cell.L) { cover(cell.c); } p.R.L = p.L; int rr = p.r - 1; data[rr / (n * n)][rr / n % n] = rr % n + 1; if (solve(depth + 1)) return true; p.L.R = p; for (cell = p.R; cell != p; cell = cell.R) resume(cell.c); p.L.R = p.R; } resume(c); return false; } public boolean solve(int data[][]) { init(data); return solve(0); } public void init(int data[][]) { solutions = new ArrayList<int[][]>(); int i, j, k; for (i = 0; i < n; ++i) for (j = 0; j < n; ++j) { if (data[i][j] > 0) { addNode(i, j, data[i][j]); } else { for (k = 1; k <= n; ++k) addNode(i, j, k); } } } public void setNum(int num) { this.num = num; } public int getNum() { return num; } public List<int[][]> getSolutions() { return solutions; } } class DLXNode { int r,c; DLXNode U,D,L,R; DLXNode() { r = c = 0; } DLXNode(int r, int c) { this.r = r; this.c = c; } DLXNode(int r, int c, DLXNode U, DLXNode D, DLXNode L, DLXNode R) { this.r = r; this.c = c; this.U = U; this.D = D; this.L = L; this.R = R; } public void delLR() { L.R = R; R.L = L; } public void delUD() { U.D = D; D.U = U; } public void resumeLR() { L.R = R.L = this; } public void resumeUD() { U.D = D.U = this; } }
import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.LineBorder; import javax.swing.border.TitledBorder; import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.text.*; public class Sudoku extends JPanel { /** * */ private static final long serialVersionUID = 1L; private static final int width = 9; private static final int height = 9; private int data[][] = new int[height][width]; private String infoStr = ""; JButton solve = new JButton("Solve"), reset = new JButton("Reset"), clean = new JButton("Clean"); BasicArrowButton right = new BasicArrowButton(BasicArrowButton.EAST), left = new BasicArrowButton(BasicArrowButton.WEST); JTextField sudokus[][] = new JTextField[height][width]; JTextPane info = new JTextPane(); java.util.List<int[][]> solutions; private int solutionIdx = 0; Sudoku() { setLayout(null); for (int r = 0; r < 3; ++r) { for (int c = 0; c < 3; ++c) { JPanel jp = new JPanel(); jp.setLayout(null); jp.setBounds(r * 95 + 5, c * 95 + 5, 95, 95); jp.setBorder(new LineBorder(Color.black)); for (int x = 0; x < 3; ++x) for (int y = 0; y < 3; ++y) { int j = r * 3 + x, i = c * 3 + y; sudokus[i][j] = new JTextField(); sudokus[i][j].setBounds(30 * x + 5, 30 * y + 5, 25, 25); sudokus[i][j].setDocument(new NumberLenghtLimitedDmt(1)); jp.add(sudokus[i][j]); } add(jp); } } solve.setBounds(85, 300, 75, 25); solve.addActionListener(new SolveAL()); add(solve); reset.setBounds(175, 300, 75, 25); reset.addActionListener(new ResetAL()); add(reset); clean.setBounds(265, 300, 75, 25); clean.addActionListener(new cleanAL()); add(clean); left.setVisible(false); left.setBounds(170, 330, 50, 25); left.addActionListener(new prevAL()); add(left); right.setVisible(false); right.setBounds(230, 330, 50, 25); right.addActionListener(new nextAL()); add(right); JScrollPane jsp = new JScrollPane(info); info.setEditable(false); jsp.setBounds(300, 5, 140, 285); jsp.setBorder(new TitledBorder("Info")); add(jsp); } public boolean solve() { int i, j; for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { try { data[i][j] = Integer.parseInt(sudokus[i][j].getText()); sudokus[i][j].setEditable(false); } catch (NumberFormatException e) { data[i][j] = 0; sudokus[i][j].setEnabled(false); } } } int n = 9; long startTime = System.currentTimeMillis(); DLX dlx = new DLX(n * n * n + 1, 4 * n * n); dlx.setNum(5); dlx.solve(data); solutions = dlx.getSolutions(); if (solutions.size() > 1) infoStr += "Find multi solutions.\n"; else if (solutions.size() > 0) infoStr += "Find one solution.\n"; else infoStr += "Cannot find a solution.\n"; infoStr += "Used time: " + (System.currentTimeMillis() - startTime) + " ms\n"; return update(); } public void reset(int data[][]) { solve.setEnabled(true); int i, j; for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { sudokus[i][j].setEnabled(true); sudokus[i][j].setEditable(true); if (data[i][j] == 0) { sudokus[i][j].setText(""); } else { sudokus[i][j].setText(Integer.toString(data[i][j])); } } } } boolean update() { info.setText(infoStr); if (solutionIdx >= solutions.size()) return false; left.setVisible(true); right.setVisible(true); int i, j; int solution[][] = solutions.get(solutionIdx); for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { sudokus[i][j].setText(Integer.toString(solution[i][j])); } } return true; } public static void inFrame(JPanel jp, int width, int height) { String title = "Sudoku"; JFrame frame = new JFrame(title); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.getContentPane().add(jp, BorderLayout.CENTER); frame.setLocationByPlatform(true); frame.setSize(width, height); frame.setResizable(false); frame.setVisible(true); } public static void main(String[] args) { int data[][] = { { 8, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 3, 6, 0, 0, 0, 0, 0 }, { 0, 7, 0, 0, 9, 0, 2, 0, 0 }, { 0, 5, 0, 0, 0, 7, 0, 0, 0 }, { 0, 0, 0, 0, 4, 5, 7, 0, 0 }, { 0, 0, 0, 1, 0, 0, 0, 3, 0 }, { 0, 0, 1, 0, 0, 0, 0, 6, 8 }, { 0, 0, 8, 5, 0, 0, 0, 1, 0 }, { 0, 9, 0, 0, 0, 0, 4, 0, 0 }, }; Sudoku sudoku = new Sudoku(); sudoku.reset(data); inFrame(sudoku, 450, 400); } class SolveAL implements ActionListener { public void actionPerformed(ActionEvent e) { infoStr += "Start solve this sudoku.\n"; info.setText(infoStr); solve.setEnabled(false); solve(); } } class ResetAL implements ActionListener { public void actionPerformed(ActionEvent e) { infoStr += "Reset sudoku data.\n"; info.setText(infoStr); left.setVisible(false); right.setVisible(false); reset(data); } } class cleanAL implements ActionListener { public void actionPerformed(ActionEvent e) { infoStr = "Clean sudoku data.\n"; info.setText(infoStr); solve.setEnabled(true); left.setVisible(false); right.setVisible(false); int i, j; for (i = 0; i < height; ++i) { for (j = 0; j < width; ++j) { sudokus[i][j].setText(""); sudokus[i][j].setEnabled(true); sudokus[i][j].setEditable(true); } } } } class prevAL implements ActionListener { public void actionPerformed(ActionEvent e) { int size = solutions.size(); if(size > 0) { solutionIdx = (solutionIdx - 1 + size) % size; infoStr += "The " + (solutionIdx+1) + "th solution.\n"; update(); } } } class nextAL implements ActionListener { public void actionPerformed(ActionEvent e) { int size = solutions.size(); if(size > 0) { solutionIdx = (solutionIdx + 1) % size; infoStr += "The " + (solutionIdx+1) + "th solution.\n"; update(); } } } class NumberLenghtLimitedDmt extends PlainDocument { /** * */ private static final long serialVersionUID = 1L; private int limit; public NumberLenghtLimitedDmt(int limit) { super(); this.limit = limit; } public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException { if (str == null) { return; } if ((getLength() + str.length()) <= limit) { char[] upper = str.toCharArray(); int length = 0; for (int i = 0; i < upper.length; i++) { if (upper[i] >= '0' && upper[i] <= '9') { upper[length++] = upper[i]; } } super.insertString(offset, new String(upper, 0, length), attr); } } } }