SP1110 SUDOKU - Sudoku题解
一道显然的 \(\text{DLX}\) 模板题。
\(\text{DLX}\) 板子,应该都会,这里注重讲一下如何建模。
建模
首先考虑一个数独,需要满足的条件。
-
每一行都需要不同。
-
每一列都需要不同。
-
每一个宫格都需要不同。
-
二百五十六个格子都不能为 \(0\) 。
注意最后一个情况,这是最容易遗漏的一个情况。
根据 \(\text{DLX}\) 性质,我们不妨将每一种情况设为每一行,而每一个限制设为每一列,就建出了模型。
考虑每一种情况
我们让 \(a_{i,j}\) 为第 \(i\) 行,第 \(j\) 列上的数。
- 当 \(a_{i,j}\) 为 \(0\) 时。
那么这个位置十六个数都有可能被填,那么就都需要加上去。
- 当 \(a_{i,j}\) 不为 \(0\) 时。
那么这个位置就只有唯一的解法。
考虑每一种限制
对于之前的情况,我们需要在不同的列中,根据限制加点。
由于有四个限制,我们可以加四个点。
代码大致如下:
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 0 + (i - 1) * 16 + j);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 1 + (i - 1) * 16 + k);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 2 + (j - 1) * 16 + k);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 3 + ((i - 1) / 4 * 4 + (j - 1) / 4) * 16 + k);
其余的就可以直接写板子了。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20000;
int n , m , t , sum , cnt , tot , a[20][20];
int l[maxn] , r[maxn] , u[maxn] , d[maxn] , h[maxn] , qx[maxn] , qy[maxn] , vy[maxn] , ans[maxn];
inline int read()
{
int s = 0 , f = 1; char ch;
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) s = s * 10 + ch - '0' , ch = getchar();
return s * f;
}
inline void init()
{
memset(l , 0 , sizeof(l));
memset(r , 0 , sizeof(r));
memset(d , 0 , sizeof(d));
memset(u , 0 , sizeof(u));
memset(h , -1 , sizeof(h));
for(int i = 0;i <= m;i++)
{
l[i] = i - 1 , r[i] = i + 1;
d[i] = u[i] = i;
}
tot = 0 , cnt = m + 1 , l[0] = m , r[m] = 0;
memset(vy , 0 , sizeof(vy));
memset(qx , 0 , sizeof(qx));
memset(qy , 0 , sizeof(qy));
memset(ans , 0 , sizeof(ans));
}
inline void add(int x , int y)
{
qx[cnt] = x , qy[cnt] = y , vy[y]++;
u[cnt] = y , d[cnt] = d[y] , u[d[y]] = cnt , d[y] = cnt;
if(h[x] < 0) h[x] = l[cnt] = r[cnt] = cnt;
else r[cnt] = h[x] , l[cnt] = l[h[x]] , l[h[x]] = r[l[h[x]]] = cnt;
cnt++;
}
inline void remove(int p)
{
l[r[p]] = l[p] , r[l[p]] = r[p];
for(int i = d[p];i != p;i = d[i])
for(int j = r[i];j != i;j = r[j])
{
vy[qy[j]]--;
u[d[j]] = u[j] , d[u[j]] = d[j];
}
return;
}
inline void resume(int p)
{
for(int i = u[p];i != p;i = u[i])
for(int j = l[i];j != i;j = l[j])
{
vy[qy[j]]++;
u[d[j]] = d[u[j]] = j;
}
l[r[p]] = r[l[p]] = p;
return;
}
inline void dfs()
{
if(!r[0])
{
sum++;
for(int i = 1;i <= tot;i++)
a[(ans[i] - 1) / 256 + 1][(ans[i] - 1) / 16 % 16 + 1] = (ans[i] - 1) % 16 + 1;
}
int p = r[0];
for(int i = r[0];i;i = r[i])
if(vy[p] > vy[i]) p = i;
remove(p);
for(int i = d[p];i != p;i = d[i])
{
ans[++tot] = qx[i];
for(int j = r[i];j != i;j = r[j]) remove(qy[j]);
dfs();
for(int j = l[i];j != i;j = l[j]) resume(qy[j]);
tot--;
}
resume(p);
}
int main()
{
t = read() , n = 4096 , m = 1024;
while(t--)
{
sum = 0 , init();
for(int i = 1;i <= 16;i++)
{
string s; cin >> s;
for(int j = 1;j <= 16;j++) if(s[j - 1] == '-') a[i][j] = 0; else a[i][j] = s[j - 1] - 'A' + 1;
for(int j = 1;j <= 16;j++)
{
for(int k = 1;k <= 16;k++)
{
if(!a[i][j] || a[i][j] == k)
{
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 0 + (i - 1) * 16 + j);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 1 + (i - 1) * 16 + k);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 2 + (j - 1) * 16 + k);
add(((i - 1) * 16 + j - 1) * 16 + k , 256 * 3 + ((i - 1) / 4 * 4 + (j - 1) / 4) * 16 + k);
}
}
}
}
dfs();
for(int i = 1;i <= 16;i++)
{
for(int j = 1;j <= 16;j++)
cout << char(a[i][j] + 'A' - 1);
cout << endl;
}
}
return 0;
}