poj 3074 Sudoku

dlx算法解數獨,有空會寫詳解,暫時就這樣了 node

Sudoku
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 7697 Accepted: 2731

Description git

In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example, 算法

. 2 7 3 8 . . 1 .
. 1 . . . 6 7 3 5
. . . . . . . 2 9
3 . 5 6 9 2 . 8 .
. . . . . . . . .
. 6 . 1 7 4 5 . 3
6 4 . . . . . . .
9 5 1 8 . . . 7 .
. 8 . . 6 5 3 4 .

Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns. 數組

Input app

The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word 「end」. ide

Output spa

For each test case, print a line representing the completed Sudoku puzzle. .net

Sample Input code

.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end

Sample Output ip

527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936

Source

[Submit]   [Go Back]   [Status]   [Discuss]

/*=============================================================================
#     FileName: 3074.cpp
#         Desc: 
#       Author: zhuting
#        Email: cnjs.zhuting@gmail.com
#     HomePage: my.oschina.net/locusxt
#      Version: 0.0.1
#    CreatTime: 2014-01-16 21:44:18
#   LastChange: 2014-01-16 21:44:18
#      History:
=============================================================================*/
/*
 * 數獨須要知足的四個條件:
 * 1.每一個格子都有數字
 * 2.每一行要有1-9
 * 3.每一列要有1-9
 * 4.每一個3*3的塊要有1-9
 *
 * 1-81列 表示某個格子有沒有填數字,填了則爲1
 * 82-162列 表示某一行有沒有1-9
 * 163-243列 表示某一列有沒有1-9
 * 244-324列 表示某個塊有沒有1-9
 *
 * 共9*9*9行
 * 表示某一個格子填了某個數後的影響
 */

#include <cstdio>
#include <cstdlib>
#include <string>
#include <cstring>
#include <algorithm>
#define maxl 100
#define maxc 325 //最大列數,包含頭結點
#define maxr 730 //最大行數,包含待匹配行
#define maxn 240000 //最大總結點數
#define head 0 //head的編號爲0
#define GET_CEL(i, j, k) i*9+j
#define GET_ROW(i, j, k) 80+i*9+k
#define GET_COL(i, j, k) 161+j*9+k
#define GET_BLO(i, j, k) 242+(3*(i/3)+j/3)*9+k
#define GET_I(l) (l-1)/81
#define GET_J(l) (l-1)%81/9
#define GET_K(l) (l-1)%81%9+1

const int m = 729;//行數
const int n = 324;//列數
bool mymap[maxr][maxc];

int num[maxc] = {0};

//數組實現的dancing links
int up[maxn] = {0}, down[maxn] = {0}, left[maxn] = {0}, right[maxn] = {0};//顧名思義是某個結點的上下左右結點的標號
int column[maxn] = {0}, row[maxn] = {0}, ans[maxn] = {0};//某個結點對應的行號,ans是取的行號的數組

char str[maxl];

/*
 * 每填一個數,假設填在數獨i行j列填的是k
 * 那麼會產生如下影響:
 * 9*j+i個cell被填上了數字
 */

//生成初始的01串
void gen_zero_one()
{
	int cur_line = 0;
	for (int i = 0; i < 9; ++i)
	{
		for (int j = 0; j < 9; ++j)
		{
			for (int k = 1; k <= 9; ++k)
			{
				mymap[cur_line][GET_CEL(i, j, k)] = 1;
				mymap[cur_line][GET_ROW(i, j, k)] = 1;
				mymap[cur_line][GET_COL(i, j, k)] = 1;
				mymap[cur_line][GET_BLO(i, j, k)] = 1;
				++cur_line;
				
			}
		}
	}
	return;
}

//生產初始的節點表
void gen_node()
{
	int id = 1;
	int tmp = 0;
	int lineup[maxc] = {0};

	for (int j = 0; j < n; ++j)//待匹配行
	{
		lineup[j] = id;
		column[id] = j + 1;
		if (j != n - 1)
		{
			right[id] = id + 1;
		}
		left[id] = id - 1;
		++id;
	}
	right[head] = 1;
	left[head] = id - 1;
	right[id - 1] = head;

	for (int i = 0; i < m; ++i)
	{
		int first_id = 0;
		int left_id = 0;
		for (int j = 0; j < n; ++j)
		{
			//scanf("%d", &tmp);
			tmp = mymap[i][j];
			if (tmp)
			{
				row[id] = i + 1;
				column[id] = j + 1;
				if (first_id == 0)
				{
					first_id = id;
					left_id = id;
				}
				else
				{
					left[id] = left_id;
					right[left_id] = id;
					left_id = id;
				}
				int upid = lineup[j];
				up[id] = upid;
				down[upid] = id;
				lineup[j] = id;
				++id;
			}
		}
		if (first_id != 0)
		{
			left[first_id] = left_id;
			right[left_id] = first_id;
		}
	}

	for (int j = 0; j < n; ++j)
	{
		down[lineup[j]] = j + 1;
		up[j + 1] = lineup[j];
	}

	//test(id);

	return;
}

void init()//初始化
{
	memset(up, 0, sizeof(up));
	memset(down, 0, sizeof(down));
	memset(left, 0, sizeof(left));
	memset(right, 0, sizeof(right));
	memset(column, 0, sizeof(column));
	memset(row, 0, sizeof(row));
	memset(ans, 0, sizeof(ans));

	memset(mymap, 0, sizeof(mymap));

	for (int i = 1; i <= n; ++i)
		num[i] = 9;
	return;
}

//這裏的remove其實並無真正刪除掉結點,能夠用resume恢復
void remove(int c)//去掉c號結點,以及在c所在列上有結點的行的結點
{
	left[right[c]] = left[c];
	right[left[c]] = right[c];

	for (int i = down[c]; i != c; i = down[i])
	{
		for (int j = right[i]; j != i; j = right[j])
		{
			up[down[j]] = up[j];
			down[up[j]] = down[j];

			--num[column[j]];
		}
	}
	return;
}

void resume(int c)//remove的逆操做
{
	left[right[c]] = c;
	right[left[c]] = c;
	for (int i = up[c]; i != c; i = up[i])
	{
		for (int j = right[i]; j != i; j = right[j])
		{
			up[down[j]] = j;
			down[up[j]] = j;

			++num[column[j]];
		}
	}
	return;
}

void recover(int t)
{
	for (int i = 0; i < t; ++i)
	{
		int tmp = ans[i];
		int cur = GET_I(tmp) * 9 + GET_J(tmp);
		str[cur] = GET_K(tmp) + '0';

		/*
		printf("%d:\n", tmp);
		printf("%d %d %d\n", GET_I(tmp), GET_J(tmp), GET_K(tmp));
		*/
	}
	return;
}

//咱們的終極目標其實是把十字鏈表中全部的結點都刪掉,只留一個頭結點
bool dance(int k)//這裏的k其實是沒多大意義的
{
	//printf("dance: %d\n", k);
	int c = right[head];//c是頭結點的右邊一個結點
	if (c == head)//只剩下十字鏈表的頭結點,則完成了目標
	{
		recover(k);
		return true;
	}
	
	int min_n = num[c];
	int min_cur = c;
	while(true)
	{
		c = right[c];
		if (c == head)
			break;
		if(num[c] < min_n)
		{
			min_n = num[c];
			min_cur = c;
		}
	}
	c = min_cur;

	remove(c);
	for (int i = down[c]; i != c; i = down[i])//這裏實際上是枚舉某一行對待匹配行的第c位進行匹配
	{
		ans[k] = row[i];
		for (int j = right[i]; j != i; j = right[j])
			remove(column[j]);//remove待匹配行的column[j]結點
		if (dance(k + 1))
			return true;
		for (int j = left[i]; j != i; j = left[j])
			resume(column[j]);
	}
	resume(c);
	return false;
}


void parse(char* a)
{
	int tmp = 0;
	for(int i = 0; i < 9; ++i)
	{
		for (int j = 0; j < 9; ++j)
		{
			if(a[i * 9 + j] != '.')
			{
				tmp = a[i * 9 + j] - '0';
				remove(GET_CEL(i, j, tmp) + 1);
				remove(GET_ROW(i, j, tmp) + 1);
				remove(GET_COL(i, j, tmp) + 1);
				remove(GET_BLO(i, j, tmp) + 1);
			}
		}
	}
	return;
}


int main()
{

	while (true)
	{
		scanf("%s", str);
		if (str[0] == 'e')
		{
			break;
		}
		
		init();
		gen_zero_one();
		gen_node();
		parse(str);
		
		int is_possible = dance(0);
		if (is_possible)
		{
			printf("%s\n", str);
		}
		else
			printf("error\n");

	}
	return 0;
}
相關文章
相關標籤/搜索