数独问题 优化 暴力->4000ms->1800ms->33ms->18ms

暴力

就是每个点都判断横竖以及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算法优化

其实舞蹈链就是模板,但是难在哪呢?就是怎么把问题转化为舞蹈链能解决的问题:精确覆盖,重复覆盖等

所以这篇题解详细讲一下舞蹈链对于数独问题的建模

那么我们先思考,数独的要求有哪些?

先梳理一下关系:

  1. 每个点只能填一个数
  2. 每个行只能填每种数各一个
  3. 每个列只能填每种数各一个
  4. 每个宫只能填每种数各一个

于是我们把点对应成一个集合,包含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;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值