暴力
就是每个点都判断横竖以及3*3的小格子 ,dfs回溯
4000ms
每次判断复杂太高,可以用3个数组标记一下,这样判断就是O(1)了
#include<bits/stdc++.h>
using namespace std;
int a[9][9];
bool row[9][10];//横向
bool col[9][10];//纵向
bool block[3][3][10];//3X3方块内的数
bool DFS(int x)
{
int r,c;
r=x/9;//横坐标
c=x%9;//纵坐标
if(x==81)
{
for(int i=0;i<9;i++)//输出
{
for(int j=0;j<9;j++)
cout<<a[i][j]<<" \n"[j==8];
}
return 1;
}
if(a[r][c]==0)
{
for(int i=1;i<=9;i++)
{
if(!(row[r][i]||col[c][i]||block[r/3][c/3][i]))//横、竖、3X3内都没有相同元素
{
a[r][c]=i;//填数
row[r][i]=col[c][i]=block[r/3][c/3][i]=1;//标记填过的数
if(DFS(x+1))return 1;//继续寻找下一个
row[r][i]=col[c][i]=block[r/3][c/3][i]=0;//找不到就返回(回溯)
a[r][c]=0;//(回溯)
}
}
return 0;
}
else return DFS(x+1);
}
char s[10][10];
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
memset(row,0,sizeof(row));
memset(col,0,sizeof(col));
memset(block,0,sizeof(block));
for(int i=0,k=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
cin>>a[i][j];
if(a[i][j]!=0)
row[i][a[i][j]]=col[j][a[i][j]]=block[i/3][j/3][a[i][j]]=1;//原来有数字的标记
}
}
DFS(0);
return 0;
}
1800ms
相对于2,每次都要对空格进行9个数字的判断,数独填到后面不会有这么多种选择,我们可以把判断改成二进制的,这样3个数组&起来,就是能填的位置
#include<bits/stdc++.h>
using namespace std;
int a[9][9];
char s[100];
int num[3000];
int row[9];//横向
int col[9];//纵向
int block[9];//3X3方块内的数
void chenge(int x,int y,int val)
{
row[x]^=val;
col[y]^=val;
block[x/3*3+y/3]^=val;
}
bool DFS(int x)
{
int r,c;
r=x/9;//横坐标
c=x%9;//纵坐标
if(x==81)
{
return 1;
}
if(a[r][c]==0)
{
for(int lock=row[r]&col[c]&block[r/3*3+c/3];lock>0;lock-=lock&(-lock))//横、竖、3X3内都没有相同元素
{
int val=lock&(-lock);
a[r][c]=num[val];//填数
chenge(r,c,val);
if(DFS(x+1))
{
return 1;
}
//(回溯)
a[r][c]=0;
chenge(r,c,val);
}
return 0;
}
else return DFS(x+1);
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
for(int i=0;i<9;i++)
{
num[1<<i]=i+1;
}
for(int i=0;i<9;i++)
{
row[i]=col[i]=block[i]=(1<<9)-1;
}
for(int i=0,k=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
cin>>a[i][j];
if(a[i][j]!=0)//原来有数字的标记
{
chenge(i,j,1<<(a[i][j]-1));
}
}
}
DFS(0);
for(int i=0;i<9;i++)//输出
{
for(int j=0;j<9;j++)
cout<<a[i][j]<<" \n"[j==8];
}
return 0;
}
33ms
数独中有些位置可以填的数的种类多,有些位置少,如果每次走到种类多的里面,会有更大概率走向无解情况,要尽量走到种类少的里面
#include <bits/stdc++.h>
using namespace std;
int f[9][9];
int cnt[1000];
int num[1000];
char s[81];
int tot;
int mapp[10][10];
int a[10], b[10], c[10];
void change(int x, int y, int v) //格子(x,y)是否能填数字v的状态取反
{
a[x]^=v;
b[y]^=v;
c[f[x][y]]^=v;
}
bool dfs(int idx)
{
if(idx==tot+1)
return true;
int Minv=10, x=0, y=0;
for(int i=0; i<9; i++)
{
for(int j=0; j<9; j++)
{
if(mapp[i][j]!=0)
continue;
int state=a[i]&b[j]&c[f[i][j]];
if(cnt[state]==0)
return false;
if(cnt[state]<Minv)
Minv=cnt[state], x=i, y=j;
}
}
int state=a[x]&b[y]&c[f[x][y]];
for(; state; state-=state&-state)
{
int val=state&-state;
int val2=num[val];
mapp[x][y]=val2;
change(x, y, val);
if(dfs(idx+1))
return true;
change(x, y, val);
mapp[x][y]=0;
}
return false;
}
void work()
{
tot=0;
for(int i=0; i<9; i++)
a[i]=b[i]=c[i]=(1<<9)-1;
for(int i=0; i<9; i++)
for(int j=0; j<9; j++)
{
int x;
scanf("%d",&x);
if(x==0)
tot++, mapp[i][j]=0;
else
mapp[i][j]=x, change(i, j, 1<<mapp[i][j]-1);
}
dfs(1);
for(int i=0; i<9; i++)
{
for(int j=0; j<9; j++)
printf("%d ", mapp[i][j]);
printf("\n");
}
}
int main()
{
for(int i=0; i<9; i++)
num[1<<i]=i+1;
for(int i=0; i<9; i++)
for(int j=0; j<9; j++)
f[i][j]=i/3*3+j/3;
for(int i=0; i<(1<<9); i++)
for(int j=i; j; j-=j&-j)
cnt[i]++;
work();
return 0;
}
18ms
相对与4,每次要9*9的判断哪些位置种类少,复杂度还是太高,我们用dancing links算法优化
其实舞蹈链就是模板,但是难在哪呢?就是怎么把问题转化为舞蹈链能解决的问题:精确覆盖,重复覆盖等
所以这篇题解详细讲一下舞蹈链对于数独问题的建模
那么我们先思考,数独的要求有哪些?
先梳理一下关系:
- 每个点只能填一个数
- 每个行只能填每种数各一个
- 每个列只能填每种数各一个
- 每个宫只能填每种数各一个
于是我们把点对应成一个集合,包含4个元素,分别表示对应的数,行,列,宫。然后精确覆盖即可。
1、矩阵中的列:
我们首先要解决第一个约束条件,那么9∗9数独中一共有81格,我们转换为81列
第1列表示(1,1)填了一个数
第2列表示(1,2)填了一个数
第3列表示(1,3)填了一个数
⋮⋮
⋮⋮
第10列表示(2,1)填了一个数
⋮⋮
⋮⋮
第81列表示(9,9)填了一个数
这样的话,我们就用了81列完成了第一个约束条件
我们接下来要解决第二个约束条件,那么9∗9数独中一共有9行,每行可以填9个数字,我们又转换为81列
第1列表示第1行填了数字1
第2列表示第1行填了数字2
第3列表示第1行填了数字3
⋮⋮
⋮⋮
第10列表示第2行填了数字1
⋮⋮
⋮⋮
第81列表示第9行填了数字9
这样的话,我们就又用了81列完成了第二个约束条件
我们接下来要解决第三个约束条件,与第二个条件类似的,9∗9数独中一共有9列,每列可以填9个数字,我们又转换为81列
第1列表示第1列填了数字1
第2列表示第1列填了数字2
第3列表示第1列填了数字3
⋮⋮
⋮⋮
第10列表示第2列填了数字1
⋮⋮
⋮⋮
第81列表示第9列填了数字9
这样的话,我们就又用了81列完成了第三个约束条件
我们接下来要解决最后一个约束条件,9∗9数独中一共有9个宫,每宫可以填9个数字,我们又转换为81列
第1列表示第1宫填了数字1
第2列表示第1宫填了数字2
第3列表示第1宫填了数字3
⋮⋮
⋮⋮
第10列表示第2宫填了数字1
⋮⋮
⋮⋮
第81列表示第9宫填了数字9
这样的话,我们就又用了81列完成了最后一个约束条件
最后,我们就用了324列来保证了每个约束条件
2、矩阵中的行
我们分两类,一种是一开始填了数字的,和没填的
对于填了数字的格子
我们举个例子:(2,1)中填了一个数7
那么我们就要把这个转换为上面的限制条件,即:
1、(2,1)中填了一个数字
2、第2行填了一个数字7
3、第1列填了一个数字7
4、第一宫填了一个数组7
我们分别与相应的列相连就可以了
对于没填数字的格子
我们举个例子:(4,5)中数字为0
那么我们就要枚举这个格子的所有情况:
这个格子填 1,像上面一样与矩阵中对应列相连
这个格子填 2,像上面一样与矩阵中对应列相连
这个格子填 3,像上面一样与矩阵中对应列相连
⋮⋮
⋮⋮
那么最后,表示(4,5)这个格子填了一个数的列的所有元素都是1,这就保证了,我填了一个数1后,这个点就不会再填其他数了(删除列操作,不多bb)
那么9∗9数独一共有81个格子,最坏的的情况下,每个格子都是0,那么每个格子都要有9种情况,所以就会有729行
我们最后把一个9∗9的数独转换为了一个729∗324的矩阵,最后就开始舞蹈链模板
#include <stdio.h>
#include <string.h>
#define N 10005
int a[10][10];
int n, m, num;
int l[N], r[N], u[N], d[N], row[N], col[N];
int h[N];
int s[N];
int ans[N];
void Init(int _n, int _m) {
n = _n; m = _m; num = m + 1;
for (int i = 0; i <= m; i++) {
l[i] = i - 1;
r[i] = i + 1;
u[i] = d[i] = i;
}
l[0] = m;
r[m] = 0;
memset(h, -1, sizeof(h));
memset(s, 0, sizeof(s));
}
void Link(int R, int C) {
++s[C];
row[num] = R;
col[num] = C;
u[num] = C;
d[num] = d[C];
u[d[C]] = num;
d[C] = num;
if (h[R] < 0) h[R] = l[num] = r[num] = num;
else {
r[num] = h[R];
l[num] = l[h[R]];
r[l[h[R]]] = num;
l[h[R]] = num;
}
++num;
}
void Remove(int c) {
l[r[c]] = l[c]; r[l[c]] = r[c];
for (int i = d[c]; i != c; i = d[i])
for (int j = r[i]; j != i; j = r[j]) {
u[d[j]] = u[j];
d[u[j]] = d[j];
--s[col[j]];
}
}
void Resume(int c) {
for (int i = u[c]; i != c; i = u[i])
for (int j = l[i]; j != i; j = l[j]) {
u[d[j]] = j;
d[u[j]] = j;
++s[col[j]];
}
l[r[c]] = r[l[c]] = c;
}
int Dance(int dep) {
if (!r[0]) {
for (int i = 0; i < dep; i++)
a[(ans[i]-1)/9/9][(ans[i]-1)/9%9] = (ans[i] - 1) % 9 + 1;
return 1;
}
int c = r[0];
for (int i = r[0]; i != 0; i = r[i]) if (s[i] < s[c]) c = i;
Remove(c);
for (int i = d[c]; i != c; i = d[i]) {
ans[dep] = row[i];
for (int j = r[i]; j != i; j = r[j]) Remove(col[j]);
if (Dance(dep+1) == 1) return 1;
for (int j = l[i]; j != i; j = l[j]) Resume(col[j]);
}
Resume(c);
return 0;
}
int main() {
Init(729, 324);
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++) {
scanf("%d", &a[i][j]);
for (int k = 1; k <= 9; k++) {
if (a[i][j] != k && a[i][j] != 0) continue;
int id = (i * 9 + j) * 9 + k;
Link(id, i * 9 + j + 1);
Link(id, i * 9 + 81 + k);
Link(id, j * 9 + 162 + k);
Link(id, 243 + (i / 3 * 3 + j / 3) * 9 + k);
}
}
Dance(0);
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
printf("%d%c", a[i][j], j + 1 == 9 ? '\n' : ' ');
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stack>
using namespace std;
int mp[10][10], cnt, visRow[10], visCol[10], visGrid[10];
pair<int, int> v[100];
struct Node {
int x, y, t;
Node(int xx, int yy, int tt) : x(xx), y(yy), t(tt) {}
};
inline int getGridId(int i, int j) { return ((i - 1) / 3) * 3 + (j - 1) / 3 + 1; }
inline int lowbit(int x) { return x & -x; }
inline int toNumber(int x) { // builtin_ctz
int i = -1;
while (x) {
x >>= 1;
++i;
}
return i;
}
void init() {
cnt = 0;
for (int i = 1; i <= 9; ++i)
visRow[i] = visCol[i] = visGrid[i] = (1 << 10) - 2;
}
void recover(stack<Node> &st, int x, int y) {
mp[x][y] = 0;
while (!st.empty()) {
int i = st.top().x, j = st.top().y, t = st.top().t;
st.pop();
visRow[i] ^= t, visCol[j] ^= t, visGrid[getGridId(i, j)] ^= t;
mp[i][j] = 0;
}
}
bool dfs(int now) {
if (now == cnt) return true;
int x = v[now].first, y = v[now].second;
// 已被填充 进入下一层
if (mp[x][y]) return dfs(now + 1);
stack<Node> st;
for (int p = now; p < cnt; ++p) {
int i = v[p].first, j = v[p].second;
if (mp[i][j]) continue;
int t = visRow[i] & visCol[j] & visGrid[getGridId(i, j)];
if (t == 0) { // 若某一空格已无选择 则回溯
recover(st, x, y);
return false;
}
if (t == lowbit(t)) { // 某一空格只有唯一选择 直接填
mp[i][j] = toNumber(t);
visRow[i] ^= t, visCol[j] ^= t, visGrid[getGridId(i, j)] ^= t;
st.push(Node(i, j, t));
}
}
for (int i = 1; i <= 9; ++i) {
for (int temp = visRow[i]; temp; temp -= lowbit(temp)) {
int t = lowbit(temp), cnt = 0, id = -1;
for (int j = 1; j <= 9; ++j) {
int choice = visRow[i] & visCol[j] & visGrid[getGridId(i, j)];
if (mp[i][j] == 0 && (choice & t)) {
++cnt, id = j;
}
}
if (cnt == 0) { // 本行没有空格能填t
recover(st, x, y);
return false;
}
if (cnt == 1) { // 本行只有id能填t
mp[i][id] = toNumber(t);
visRow[i] ^= t, visCol[id] ^= t, visGrid[getGridId(i, id)] ^= t;
st.push(Node(i, id, t));
}
}
}
for (int j = 1; j <= 9; ++j) {
for (int temp = visCol[j]; temp; temp -= lowbit(temp)) {
int t = lowbit(temp), cnt = 0, id = -1;
for (int i = 1; i <= 9; ++i) {
int choice = visRow[i] & visCol[j] & visGrid[getGridId(i, j)];
if (mp[i][j] == 0 && (choice & t)) {
++cnt;
id = i;
}
}
if (cnt == 0) { // 本列没有空格能填t
recover(st, x, y);
return false;
}
if (cnt == 1) { // 本列只有id能填t
mp[id][j] = toNumber(t);
visRow[id] ^= t, visCol[j] ^= t, visGrid[getGridId(id, j)] ^= t;
st.push(Node(id, j, t));
}
}
}
for (int g = 1; g <= 9; ++g) {
for (int temp = visGrid[g]; temp; temp -= lowbit(temp)) {
int t = lowbit(temp), cnt = 0, idx = -1, idy = -1;
for (int i = (g - 1) / 3 * 3 + 1; i <= (g - 1) / 3 * 3 + 3; ++i) {
for (int j = (g - 1) % 3 * 3 + 1; j <= (g - 1) % 3 * 3 + 3; ++j) {
int choice = visRow[i] & visCol[j] & visGrid[getGridId(i, j)];
if (mp[i][j] == 0 && (choice & t)) {
++cnt;
idx = i, idy = j;
}
}
}
if (cnt == 0) { // 本3*3没有空格能填t
recover(st, x, y);
return false;
}
if (cnt == 1) { // 本3*3只有(idx, idy)能填t
mp[idx][idy] = toNumber(t);
visRow[idx] ^= t, visCol[idy] ^= t, visGrid[getGridId(idx, idy)] ^= t;
st.push(Node(idx, idy, t));
}
}
}
if (mp[x][y]) {
if (dfs(now + 1)) return true;
} else {
for (int choice = visRow[x] & visCol[y] & visGrid[getGridId(x, y)]; choice; choice -= lowbit(choice)) {
int t = lowbit(choice);
mp[x][y] = toNumber(t);
visRow[x] ^= t, visCol[y] ^= t, visGrid[getGridId(x, y)] ^= t;
if (dfs(now + 1)) return true;
visRow[x] ^= t, visCol[y] ^= t, visGrid[getGridId(x, y)] ^= t;
}
}
recover(st, x, y);
return false;
}
int main(int argc, char const *argv[]) {
init();
for (int i = 1; i <= 9; ++i) {
for (int j = 1; j <= 9; ++j) {
scanf("%d", &mp[i][j]);
if (mp[i][j] == 0) {
v[cnt++] = make_pair(i, j);
} else {
visRow[i] ^= (1 << (mp[i][j]));
visCol[j] ^= (1 << (mp[i][j]));
visGrid[getGridId(i, j)] ^= (1 << (mp[i][j]));
}
}
}
dfs(0);
for (int i = 1; i <= 9; ++i) {
for (int j = 1; j <= 9; ++j) {
printf("%d%c", mp[i][j], " \n"[j == 9]);
}
}
return 0;
}