project euler 96

Problem 96


Su Doku

Su Doku (Japanese meaning number place) is the name given to a popular puzzle concept. Its origin is unclear, but credit must be attributed to Leonhard Euler who invented a similar, and much more difficult, puzzle idea called Latin Squares. The objective of Su Doku puzzles, however, is to replace the blanks (or zeros) in a 9 by 9 grid in such that each row, column, and 3 by 3 box contains each of the digits 1 to 9. Below is an example of a typical starting puzzle grid and its solution grid.

             
0 0 3 0 2 0 6 0 0 * 4 8 3 9 6 7 2 5 1
9 0 0 3 0 5 0 0 1 * 9 2 1 3 4 5 8 7 6
0 0 1 8 0 6 4 0 0 * 6 5 7 8 2 1 4 9 3
             
0 0 8 1 0 2 9 0 0 * 5 4 8 7 2 9 1 3 6
7 0 0 0 0 0 0 0 8 * 1 3 2 5 6 4 7 9 8
0 0 6 7 0 8 2 0 0 * 9 7 6 1 3 8 2 4 5
             
0 0 2 6 0 9 5 0 0 * 3 7 2 8 1 4 6 9 5
8 0 0 2 0 3 0 0 9 * 6 8 9 2 5 3 4 1 7
0 0 5 0 1 0 3 0 0 * 5 1 4 7 6 9 3 8 2

A well constructed Su Doku puzzle has a unique solution and can be solved by logic, although it may be necessary to employ “guess and test” methods in order to eliminate options (there is much contested opinion over this). The complexity of the search determines the difficulty of the puzzle; the example above is considered easybecause it can be solved by straight forward direct deduction.

The 6K text file, sudoku.txt(right click and ‘Save Link/Target As…’), contains fifty different Su Doku puzzles ranging in difficulty, but all with unique solutions (the first puzzle in the file is the example above).

By solving all fifty puzzles find the sum of the 3-digit numbers found in the top left corner of each solution grid; for example, 483 is the 3-digit number found in the top left corner of the solution grid above.


数独

数独(日语原意为数的位置)是一种热门的谜题。它的起源已不可考,但是与欧拉发明的一种类似而更加困难的谜题拉丁方阵之间有着千丝万缕的联系。数独的目标是替换掉9乘9网格中的空白位置(或0),使得每行、每列以及每个九宫格中恰好都包含数字1~9。如下是一个典型的数独谜题以及它的解答。

             
0 0 3 0 2 0 6 0 0 * 4 8 3 9 6 7 2 5 1
9 0 0 3 0 5 0 0 1 * 9 2 1 3 4 5 8 7 6
0 0 1 8 0 6 4 0 0 * 6 5 7 8 2 1 4 9 3
             
0 0 8 1 0 2 9 0 0 * 5 4 8 7 2 9 1 3 6
7 0 0 0 0 0 0 0 8 * 1 3 2 5 6 4 7 9 8
0 0 6 7 0 8 2 0 0 * 9 7 6 1 3 8 2 4 5
             
0 0 2 6 0 9 5 0 0 * 3 7 2 8 1 4 6 9 5
8 0 0 2 0 3 0 0 9 * 6 8 9 2 5 3 4 1 7
0 0 5 0 1 0 3 0 0 * 5 1 4 7 6 9 3 8 2

一个构造精良的数独谜题应该包含有唯一解,且能够通过逻辑推断来解决,尽管有时可能必须通过“猜测并检验”来排除一些选项(这一要求目前还颇受争议)。寻找答案的复杂度决定了题目的难度;上面这个谜题被认为是简单的谜题,因为我们可以通过直截了当的演绎推理来解决它。

在这个6K的文本文件sudoku.txt(右击并选择“目标另存为……”)中包含有50个不同难度的数独谜题,但保证它们都只有唯一解(文件中的第一个谜题就是上述样例)。

解开这50个谜题,找出每个谜题解答左上角的三个数字并连接起来,给出这些数的和;举例来说,上述样例解答左上角的三个数字连接起来构成的数是483。

package projecteuler;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

public class Prj96 extends TestCase {

	private static final String PATH = "E:\\whua\\mathWorkspace\\test_jgraph\\src\\projecteuler\\Prj96.txt";

	public void testSuDoku() {

		List<int[][]> dataList = readDatas();

		int sum = 0;

		for (int i = 0; i < dataList.size(); i++) {

			int[][] data = dataList.get(i);

			DfsSudoKu dfs = new DfsSudoKu();
			dfs.readInputData(data);
			dfs.DFS(0);

			sum += (dfs.copyTable[0][0] * 100 + dfs.copyTable[0][1] * 10 + dfs.copyTable[0][2]);
		}

		System.out.println("sum=" + sum);
	}

	public class DfsSudoKu {

		private boolean[][] rUsed, cUsed, sUsed;
		private int[] pos;
		private int nullNum;
		private boolean DFS_SUDO;

		private int[][] table;
		public int[][] copyTable;

		private void print() {

			copyTable = new int[9][9];
			for (int i = 0; i < 9; i++) {

				for (int j = 0; j < 9; j++) {

					System.out.print(table[i][j] + " ");
					copyTable[i][j] = table[i][j];
				}
				System.out.println();
			}
			System.out.println("+++++++++++++++++++");
		}

		public void DFS(int n) {

			if (n >= nullNum) {
				DFS_SUDO = true;
				print();
				return;
			}

			int r = pos[n] / 9;
			int c = pos[n] % 9;
			int k = (r / 3) * 3 + (c / 3);

			for (int i = 1; i <= 9 && !DFS_SUDO; i++) {

				if (cUsed[c][i])
					continue;

				if (rUsed[r][i])
					continue;

				if (sUsed[k][i])
					continue;

				cUsed[c][i] = rUsed[r][i] = sUsed[k][i] = true;
				table[r][c] = i;

				DFS(n + 1);

				table[r][c] = 0;
				cUsed[c][i] = rUsed[r][i] = sUsed[k][i] = false;
			}

		}

		public void readInputData(int[][] data) {

			nullNum = 0;
			DFS_SUDO = false;

			rUsed = new boolean[9][];
			cUsed = new boolean[9][];
			sUsed = new boolean[9][];

			pos = new int[100];
			table = new int[9][];

			for (int i = 0; i < 9; i++) {

				rUsed[i] = new boolean[10];
				cUsed[i] = new boolean[10];
				sUsed[i] = new boolean[10];

				table[i] = new int[9];
			}

			for (int i = 0; i < 9; i++) {
				for (int j = 0; j < 10; j++) {

					rUsed[i][j] = cUsed[i][j] = sUsed[i][j] = false;
				}
			}

			for (int i = 0; i < 9; i++) {
				for (int j = 0; j < 9; j++) {

					table[i][j] = data[i][j];
					if (table[i][j] != 0) {

						rUsed[i][table[i][j]] = true;
						cUsed[j][table[i][j]] = true;

						int k = (i / 3) * 3 + (j / 3);

						sUsed[k][table[i][j]] = true;
					} else {
						pos[nullNum++] = 9 * i + j;
					}
				}
			}
		}
	}

	List<int[][]> readDatas() {

		List<int[][]> ret = new ArrayList<int[][]>();
		try {

			BufferedReader reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(PATH), "utf8"));

			int i = 50;

			while (i > 0) {
				reader.readLine();

				String str1 = reader.readLine().trim();
				String str2 = reader.readLine().trim();
				String str3 = reader.readLine().trim();
				String str4 = reader.readLine().trim();
				String str5 = reader.readLine().trim();
				String str6 = reader.readLine().trim();
				String str7 = reader.readLine().trim();
				String str8 = reader.readLine().trim();
				String str9 = reader.readLine().trim();

				int[][] data = new int[9][];
				data[0] = str2IntArr(str1);
				data[1] = str2IntArr(str2);
				data[2] = str2IntArr(str3);
				data[3] = str2IntArr(str4);
				data[4] = str2IntArr(str5);
				data[5] = str2IntArr(str6);
				data[6] = str2IntArr(str7);
				data[7] = str2IntArr(str8);
				data[8] = str2IntArr(str9);
				ret.add(data);
				i--;
			}

			reader.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return ret;

	}

	private int[] str2IntArr(String str) {
		int[] ret = new int[9];
		for (int i = 0; i < 9; i++) {
			ret[i] = Integer.parseInt(String.valueOf(str.charAt(i)));
		}
		return ret;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值