poj 3074 Sudoku

dlx算法解数独,有空会写详解,暂时就这样了

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

Description

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

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”.

Output

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

Sample Input

.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

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;
}




转载于:https://my.oschina.net/locusxt/blog/193922

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值