DLX UVA 1461 - Sudoku Extension

Sudoku is a logic-based, combinatorial number-placement puzzle. The objective is to fill a 9 x 9 grid so that each column, each row, and each of the nine 3 x 3 boxes (also called blocks or regions) contains the digits from 1 to 9 only one time each. The puzzle setter provides a partially completed grid.[from wikipedia]

Left figure is the puzzle and right figure is one solution for that puzzle.

\epsfbox{p4763.eps}

It's a very common game for magazine and newspapers, and people like it for killing time. How about adding some features to this game, some cells can only be filled with even number, some cells can only be filled with odd number, and some cells can only be filled with the same number. Great, let's call it ``Sudoku Extension".

It's very easy to give one possible solution a Sudoku Extension puzzle, but do you know how many solutions are there for a certain Sudoku Extension problem? Let's find out!

Input 

The input data will start with the number of test cases. For each test case, 9 lines followed, corresponding to the rows of the table. On each line, a string of exactly 9 characters is given, it's either 0-9 digit, or a-z alpha in lower case. `1' - `9' means the cell is already filled in; `0' means this cell is empty; `e' means the cell need to be filled with a even number; `o' means the cell need to be filled with an odd number; other alpha of `a' - `z', except `e' and `o' denote a variable, so if two or more cells denoted by `a', then they must be filled with the same number.

Output 

The output for each case is the number of possible solutions, note for each case, the solution will be less than 3000, and there are not more than 10 test cases.

Sample Input 

1 
040008007 
00e030408 
006000200 
301004000 
0080000o2 
000003000 
200401005 
600070000 
800006003

Sample Output 

507

题意:就是数独填数字,o的地方只能填奇数,e的地方只能填偶数,相同字母的地方只能填相同的数字,0表示能填1~9,。问一共有多少填法。


思路:第一道用DLX做出来的题目。DLX就是一个十字链表。有上下左右的指针。很难解释,找找资料。这个非常重要的一个用法就是用来解决最小覆盖问题,就是选取一定的集合,使得每个元素出现且仅出现一次。但是这个数独怎么用DLX来做呢?我们首先要把数独问题转化为最小覆盖问题。在DLX中行代表的是选取的状态(r,c,v)表示在(r,c)格子填入v数字。如果选取了这行,就表示执行了这个操作。列的话表示要满足的条件。我们有四个状态:1.(r,c)要填入数字 2.(r,v) 第r行要有数字v 3.(c,v)第c列要有数字v。4.(area,v)在第area分块要有数字v。 

所以行一共有9*9*9 (r,c,v)每个数都是9种可能 , 一共(9*9+9*9+9*9+9*9)列, 同理。所以我们有一个729*324的0-1矩阵。 在搜索的时候当我们选取了一列表示,我们要想办法满足这个列的这个状态,那么我们从所有能够满足的这个列的行状态(就是在哪个格子填什么数字)中选一个,选中后,把其他行删除,同时要把这个行中能满足的列状态全部删除,因为已经满足了。继续搜下去,知道所有列状态都满足,那么就搜出了答案。有一个优化就是我们选取列的时候,我们要选一个1最少的。因为最少代表最迫切要满足的,以后可能就不能满足了。

回到这个题目,这里面有一些限制条件,我们只需要根据这些构建DLX就行了。例如如果在(r,c)为0,那么我们这个格子能选v(1<=v<=9),那么当我们选(r,c,v)的时候,他对应能满足的状态有1.第r行需要数字v。 2.第c列需要数字v 。 3.第r行第c列需要有数字。 4.第r/3*3+c/3区块需要数字v。 把这几个状态对应的添加进去。其他同理的。


代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<cstring>
#include<string>
#include<set>
#include<vector>
using namespace std;
const int maxn = (9*9*9*9*9*4)+10;
const int n = 9;
char puzzle[n+5][n+5];
const int SLOT = 0;
const int COL = 2;
const int ROW = 1;
const int SUB = 3;
vector<int> vec[9][9];

char solution[15*15];
set<string> ss;
inline int encode(int a,int b,int c) 
{
	return a*81+b*9+c+1;
}

void decode(int code,int &a,int &b,int &c)
{
	--code;
	c = code%9; code /= 9;
	b = code%9; code /= 9;
	a = code;
}

struct DLX 
{
	int n , sz;
	int S[maxn];
	int row[maxn] , col[maxn];
	int L[maxn] , R[maxn] , U[maxn] , D[maxn];
	int ansd, ans[maxn];

	void init(int n) {
		this->n = n;
		for (int i = 0 ; i <= n ; ++i) {
			U[i] = i;
			D[i] = i;
			L[i] = i-1;
			R[i] = i+1;
		}
		R[n] = 0; L[0] = n;
		sz = n+1;
		memset(S,0,sizeof(S));
	}

	void addRow(int r,vector<int> columns) {
		int first = sz;
		for (int i = 0 ; i < columns.size() ; ++i) {
			int c = columns[i];
			L[sz] = sz-1; R[sz] = sz+1;
			D[sz] = c;  U[sz] = U[c];
			D[U[c]] = sz; U[c] = sz;
			row[sz] = r; col[sz] = c;
			++S[c]; sz++;
		}
		R[sz-1] = first; L[first] = sz-1;
	}
#define FOR(i,A,s) for(int i = A[s] ; i != s ; i = A[i])
	void remove(int c) {
		L[R[c]] = L[c];
		R[L[c]] = R[c];
		FOR(i,D,c)
			FOR(j,R,i) { 
				U[D[j]] = U[j]; 
				D[U[j]] = D[j]; 
				--S[col[j]]; 
		}
	}

	void restore(int c) {
		FOR(i,U,c)
			FOR(j,L,i) { ++S[col[j]]; U[D[j]] = j; D[U[j]] = j; }
		L[R[c]] = c;
		R[L[c]] = c;
	}

	void dfs(int d) {
		if (R[0]==0) {
			ansd = d;
			string s = solution;
			ss.insert(s);
			return;
		}
		int c = R[0];
		FOR(i,R,0) if (S[i] < S[c]) c = i;
		remove(c);
		int ret = 0;
		FOR(i,D,c) {
			ans[d] = row[i];
			int r , cc , v;
			decode(row[i],r,cc,v);
			solution[r*9+cc] = v+'1';
			FOR(j,R,i)
				remove(col[j]);
			for (int j = 0 ; j < vec[r][cc].size() ; ++j) 
				solution[vec[r][cc][j]] = v+'1';
			dfs(d+1);
			FOR(j,L,i) restore(col[j]);
		}
		restore(c);
	}

}solver;

bool isalpha(char ch) 
{
	return 'a'<=ch&&ch<='z'&&ch!='e'&&ch!='o';
}

bool isdigit(char ch) 
{
	return '1'<=ch&&ch<='9';
}

int main()
{
	int T; cin>>T;
	while (T--) {
		for (int i = 0 ; i < 9 ; ++i) scanf("%s",puzzle[i]);
		solver.init(9*9*4);
		vector<pair<int,int> > alpha[30];
		for (int r = 0 ; r < 9 ; ++r)
		{
			for (int c = 0 ; c < 9 ; ++c) 
			{
				char ch = puzzle[r][c];
				if (isalpha(ch)) alpha[ch-'a'].push_back(make_pair(r,c));
				if (isdigit(ch)) {
					vector<int> column;
					column.push_back(encode(SLOT,r,c));
					column.push_back(encode(ROW,r,ch-'1'));
					column.push_back(encode(COL,c,ch-'1'));
					column.push_back(encode(SUB,r/3*3+c/3,ch-'1'));
					solver.addRow(encode(r,c,ch-'1'),column);
					continue;
				}
				if (ch=='0') {
					for (int v = 1 ; v <= 9 ; ++v) {
						vector<int> column;
						column.push_back(encode(SLOT,r,c));
						column.push_back(encode(ROW,r,v-1));
						column.push_back(encode(COL,c,v-1));
						column.push_back(encode(SUB,(r/3)*3+c/3,v-1));
						solver.addRow(encode(r,c,v-1),column);
					}
				}
				if (puzzle[r][c]=='o') {
					for (int v = 1 ; v <= 9 ; v+=2) {
						vector<int> column;
						column.push_back(encode(SLOT,r,c));
						column.push_back(encode(ROW,r,v-1));
						column.push_back(encode(COL,c,v-1));
						column.push_back(encode(SUB,(r/3)*3+c/3,v-1));
						solver.addRow(encode(r,c,v-1),column);
					}
				}
				if (puzzle[r][c]=='e') {
					for (int v = 2 ; v <= 9 ; v+=2) {
						vector<int> column;
						column.push_back(encode(SLOT,r,c));
						column.push_back(encode(ROW,r,v-1));
						column.push_back(encode(COL,c,v-1));
						column.push_back(encode(SUB,(r/3)*3+c/3,v-1));
						solver.addRow(encode(r,c,v-1),column);
					}
				}
			}
		}
		for (int i = 0 ; i < 26 ; ++i) {
			if (alpha[i].empty()) continue;
			for (int v = 1 ; v <= 9 ; ++v) {
				vector<int> column;
				int r , c;
				for (int j = 0 ; j < alpha[i].size() ; ++j) {
				    r = alpha[i][j].first , c = alpha[i][j].second;
					column.push_back(encode(SLOT,r,c));
					column.push_back(encode(ROW,r,v-1));
					column.push_back(encode(COL,c,v-1));
					column.push_back(encode(SUB,r/3*3+c/3,v-1));
				}
				vec[r][c].clear();
				for (int j = 0 ; j < alpha[i].size() ; ++j) 
					vec[r][c].push_back(alpha[i][j].first*9+alpha[i][j].second);
				solver.addRow(encode(r,c,v-1),column);
			}
		}
		memset(solution,0,sizeof(solution));
		ss.clear();
		solver.dfs(0);
		printf("%d\n",ss.size());
	}
}

 




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值