数独求解(靠逻辑解数独)
数独求解
之前写过一个数独的题解,但是那个题解里各个函数是分散的。下面给出完整代码。能够解不需要枚举的数独而是靠逻辑一步步解出数独。
给下面一个例子:
board{{0,0,7,0,0,5,0,0,3},
{0,0,0,6,3,0,0,0,0},
{5,6,0,0,0,0,9,2,0},
{0,0,0,1,2,9,0,0,0},
{4,0,0,0,0,0,0,7,0},
{2,0,0,0,0,0,0,8,0},
{0,0,4,0,0,3,0,0,1},
{0,0,0,8,7,0,0,0,0},
{8,9,0,0,0,0,3,5,0}};
当这样时传入函数,解不出来,但是通过自己先推出两个数字后,也就是:
{0,0,0,6,3,0,0,0,0},
{5,6,0,0,0,0,9,2,0},
{0,0,0,1,2,9,0,0,0},
{4,0,0,0,0,0,0,7,0},
{2,0,0,0,0,0,0,8,0},
{0,0,4,0,0,3,0,0,1},
{0,0,0,8,7,0,0,0,0},
{8,9,0,0,0,0,3,5,0}};
当这样时传入函数,解不出来,但是通过自己先推出两个数字后,也就是:
board{{0,0,7,0,0,5,0,0,3},
{9,0,0,6,3,0,0,0,0},
{5,6,0,0,0,0,9,2,0},
{0,0,0,1,2,9,0,0,0},
{4,0,0,0,0,0,0,7,0},
{2,0,0,0,0,0,0,8,0},
{0,0,4,0,0,3,0,0,1},
{0,0,0,8,7,0,0,9,0},
{8,9,0,0,0,0,3,5,0}};
{9,0,0,6,3,0,0,0,0},
{5,6,0,0,0,0,9,2,0},
{0,0,0,1,2,9,0,0,0},
{4,0,0,0,0,0,0,7,0},
{2,0,0,0,0,0,0,8,0},
{0,0,4,0,0,3,0,0,1},
{0,0,0,8,7,0,0,9,0},
{8,9,0,0,0,0,3,5,0}};
而添加进去的数字逻辑函数没有写,所以运行不出来。这个逻辑是什么呢,通过第3,4,9行的数字9就可以推出来
第二行第一个是9,而第8行的9通过3,4,9行确定第7行中间格有9(不确定具体在那儿)但是确定第7行的9不在
第9格,那么第8行的9位置就确定了。然后当填进去这两个9后,运行结果就出来了。而这个逻辑函数一时半会
还真不好写。也就是还没有关于格的函数,只要增加这个函数,那么所有不需要枚举的数独都能够求解。
关于每个函数的作用和函数需要注意的地方可以查看数独题解http://www.nowcoder.com/practice/5e6c424b82224b85b64f28fd85761280
下面给出运行结果:
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <iterator>
#include <vector>
using namespace std;
//求两个集合的交集
void Intersection(vector<int> &lhs,vector<int> &rhs,vector<int> &R){
R.clear();
sort(lhs.begin(),lhs.end());
sort(rhs.begin(),rhs.end());
set_intersection(lhs.begin(),lhs.end(),rhs.begin(),rhs.end(),back_inserter(R));
}
//求集合的差集
void Difference(vector<int> &lhs,vector<int> &rhs,vector<int> &R){
R.clear();
R.resize(lhs.size());
vector<int>::iterator retEndPos;
retEndPos=set_difference(lhs.begin(),lhs.end(),rhs.begin(),rhs.end(),R.begin());
R.resize(retEndPos-R.begin());
}
//修改行,列,格
void ChangeHLG(int i,int j,vector<vector<int> > &H,vector<vector<int> > &L,vector<vector<int> > &G,int r){
vector<int>::iterator it;
it=find(H[i].begin(),H[i].end(),r);//寻找对应元素在行中的位置
H[i].erase(it);//在行中删除对应元素
it=find(L[j].begin(),L[j].end(),r);//寻找对应元素在列中的位置
L[j].erase(it);//在列中删除元素
it=find(G[j/3+i/3*3].begin(),G[j/3+i/3*3].end(),r);//寻找对应元素在格中的位置
G[j/3+i/3*3].erase(it);//在格中删除元素
}
//*******/
int Check_num(vector<vector<int> > &board,int c,int i,int j) {
int c_num=0;
vector<int>::iterator it;
if(i%3==0) {
for(int k=0;k<9;++k) {
if(board[i+1][k]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[i+2][k]==c) {
++c_num;
break;
}}
}
else if(i%3==1) {
for(int k=0;k<9;++k) {
if(board[i+1][k]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[i-1][k]==c) {
++c_num;
break;
}}
}
else {
for(int k=0;k<9;++k) {
if(board[i-1][k]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[i-2][k]==c) {
++c_num;
break;
}}
}
if(j%3==0) {
for(int k=0;k<9;++k) {
if(board[k][j+2]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[k][j+1]==c) {
++c_num;
break;
} }
}
else if(j%3==1) {
for(int k=0;k<9;++k) {
if(board[k][j+1]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[k][j-1]==c) {
++c_num;
break;
} }
}
else {
for(int k=0;k<9;++k) {
if(board[k][j-2]==c) {
++c_num;
break;
} }
for(int k=0;k<9;++k) {
if(board[k][j-1]==c) {
++c_num;
break;
} }
}
return c_num;
}
bool check_num2(vector<vector<int> > &board,int c,int i,int j) {
bool l=false;
int num=0,num_c=0;
int num2=0,num_c2=0;
for(int k=0;k<9;++k) {
if(board[i][k]==0){
++num;
for(int m=0;m<9;++m) {
if(board[m][k]==c) {
++num_c;
break;}}
}
}
for(int k=0;k<9;++k) {
if(board[k][j]==0){
++num2;
for(int m=0;m<9;++m) {
if(board[k][m]==c){
++num_c2;
break;}
}
}
}
if(num-1==num_c||num2-1==num_c2) l=true;
return l;
}
bool solveSudoku(vector<vector<int> > &board,vector<vector<int> > &H,vector<vector<int> > &L,
vector<vector<int> > &G,vector<vector<vector<int> > > &IJ_R,int &num) {
vector<int> R;//交集
vector<int> tmp;
int k,c_num=0;
int c;
bool J=true;
while(num>0) {
k=num;
for(int i=0;i<9;++i) {
for(int j=0;j<9;++j) {
if(board[i][j]==0) {
tmp.clear();
R.clear();
Intersection(H[i],L[j],tmp);//求得行、列的交集
Intersection(tmp,G[j/3+i/3*3],R);//求得行,列,格的交集
if(R.size()==1) {//交集长度为1,说明该点是确定的值。
board[i][j]=R[0];//修改数独中的数字
ChangeHLG(i,j,H,L,G,R[0]);
--num;
}
else if(R.size()>=2){
for(vector<int>::iterator it=R.begin();it!=R.end();++it) {
c=*it;
if(check_num2(board,c,i,j)) {
board[i][j]=c;
ChangeHLG(i,j,H,L,G,c);
R.clear();
R.push_back(c);
--num;
break;
}
c_num=Check_num(board,c,i,j);
if(4==c_num) {
board[i][j]=c;
ChangeHLG(i,j,H,L,G,c);
R.clear();
R.push_back(c);
--num;
break;
} }
}
else {J=false;return J;}
IJ_R[i][j].clear();
IJ_R[i][j]=R;
}
}
}
if(k==num) break;//防止陷入死循环
}
return J;
}
void Diff(vector<vector<int> > &board,vector<vector<int> > &H,vector<vector<int> > &L,
vector<vector<int> > &G,vector<vector<vector<int> > > &IJ_R,int &num){
vector<int>::size_type ij_size,ji_size;
vector<int> R;
int k;
while(num>0){
k=num;
for(int i=0;i<9;++i){
for(int j=0;j<9;++j){
if(IJ_R[i][j].size()>1){
ij_size=0,ji_size=0;
for(int k=j;k<9;++k)
if(IJ_R[i][j]==IJ_R[i][k]) ++ij_size;
for(int k=i;k<9;++k)
if(IJ_R[k][j]==IJ_R[i][j]) ++ji_size;
if(ij_size==IJ_R[i][j].size()){
for(int k=0;k<9;++k) {
if(IJ_R[i][k]!=IJ_R[i][j]&&IJ_R[i][k].size()>1){
Difference(IJ_R[i][k],IJ_R[i][j],R);
if(R.size()==1) {
board[i][k]=R[0];
ChangeHLG(i,k,H,L,G,R[0]);
--num;
}
IJ_R[i][k].clear();
IJ_R[i][k]=R;
}
}
break;
}
if(ji_size==IJ_R[i][j].size()){
for(int k=0;k<9;++k) {
if(IJ_R[k][j]!=IJ_R[i][j]&&IJ_R[k][j].size()>1){
Difference(IJ_R[k][j],IJ_R[i][j],R);
if(R.size()==1) {
board[k][j]=R[0];
ChangeHLG(k,j,H,L,G,R[0]);
--num;
}
IJ_R[k][j].clear();
IJ_R[k][j]=R;
}
}
}
}
}
}
// solveSudoku(board,H,L,G,IJ_R,num);
if(num==k) break;//防止陷入死循环
}
}
int main() {
vector<vector<int> > board{{0,0,7,0,0,5,0,0,3},
{9,0,0,6,3,0,0,0,0},
{5,6,0,0,0,0,9,2,0},
{0,0,0,1,2,9,0,0,0},
{4,0,0,0,0,0,0,7,0},
{2,0,0,0,0,0,0,8,0},
{0,0,4,0,0,3,0,0,1},
{0,0,0,8,7,0,0,9,0},
{8,9,0,0,0,0,3,5,0}};
vector<int> ivec{1,2,3,4,5,6,7,8,9};
vector<vector<int> > IR{{1},{2},{3},{4},{5},{6},{7},{8},{9}};
vector<int> R;//交集
vector<int> tmp;
int num=0,numtmp=0;
vector<int>::iterator it;
vector<vector<int> > G;//格可填数字的集合
vector<vector<int> > H;//行可填数字集合
vector<vector<int> > L;//列可填数字集合
vector<vector<vector<int> > > IJ_R;//所有元素的交集
vector<vector<int> > HL_R;//行的交集。
for(int i=0;i<9;++i) {
G.push_back(ivec);//开始初始化9个格的集合为1~9
L.push_back(ivec);//开始初始化9个列的集合为1~9
H.push_back(ivec);//开始初始化9个行的集合为1~9
}
for(int i=0;i<9;++i) {
HL_R.clear();
for(int j=0;j<9;++j) {
if(board[i][j]) {
ChangeHLG(i,j,H,L,G,board[i][j]);
R.clear();
R=IR[board[i][j]-1];
HL_R.push_back(R);//交集为单个元素
}
else {
++num;
R.clear();
R=ivec;
HL_R.push_back(R);
}
}
IJ_R.push_back(HL_R);
}//以上完成数独的预处理
while(num>0) {
numtmp=num;
solveSudoku(board,H,L,G,IJ_R,num);
Diff(board,H,L,G,IJ_R,num);
if(num==numtmp) break;//防止陷入死循环
}
for(int i=0;i<9;++i){
for(int j=0;j<9;++j)
cout<<board[i][j]<<",";
cout<<endl;
}
for(int i=0;i<9;++i) {
cout<<"The "<<i<<"'s intersection:"<<endl;
for(int j=0;j<9;++j) {
for(it=IJ_R[i][j].begin();it!=IJ_R[i][j].end();++it)
cout<<*it<<",";
cout<<endl;
}
}
R.clear(), H.clear(),G.clear(),tmp.clear();
HL_R.clear(),IJ_R.clear();
ivec.clear(),board.clear();
return 0;
}
查看6道真题和解析
智元机器人成长空间 174人发布