(AC代码在最后面)
Description
In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,
. | 2 | 7 | 3 | 8 | . | . | 1 | . |
. | 1 | . | . | . | 6 | 7 | 3 | 5 |
. | . | . | . | . | . | . | 2 | 9 |
3 | . | 5 | 6 | 9 | 2 | . | 8 | . |
. | . | . | . | . | . | . | . | . |
. | 6 | . | 1 | 7 | 4 | 5 | . | 3 |
6 | 4 | . | . | . | . | . | . | . |
9 | 5 | 1 | 8 | . | . | . | 7 | . |
. | 8 | . | . | 6 | 5 | 3 | 4 | . |
Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.
Input
The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.
Output
For each test case, print a line representing the completed Sudoku puzzle.
Sample Input
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936
如果按照POJ2676 Sudoku、POJ2918 Tudoku(数独(一))做是行不通的,需要进一步优化
超时代码:
//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define e 2.71828182
#define Pi 3.141592654
using namespace std;
char table[10][10];
int row[10][10],col[10][10],grid[10][10];
bool flag=false;
void dfs(int x,int y)//填第(x,y)格
{
if(flag) return;//已找到一种解法
if(y>9) y=1,x+=1;
if(x>9)
{
for(int i=1;i<=9;i++)
cout<<table[i]+1;
flag=true;
return;
}
if(table[x][y]!='0') dfs(x,y+1);
else if(table[x][y]=='0')
{
int ord=(x-1)/3*3+(y-1)/3+1;//第几个方块
for(int i=1;i<=9;i++)
{
if(!row[x][i]&&!col[y][i]&&!grid[ord][i])
{
table[x][y]=i+'0';
row[x][i]=1;col[y][i]=1;grid[ord][i]=1;
dfs(x,y+1);
//回溯
table[x][y]='0';
row[x][i]=0;col[y][i]=0;grid[ord][i]=0;
}
}
}
}
int main()
{
string str;
while(cin>>str)
{
if(str=="end") break;
int count=0;
mem(row,0);mem(col,0);mem(grid,0);
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
table[i][j]=str[count++];
if(table[i][j]=='.') table[i][j]='0';
row[i][table[i][j]-'0']=1;
col[j][table[i][j]-'0']=1;
int ord=(i-1)/3*3+(j-1)/3+1;
grid[ord][table[i][j]-'0']=1;
}
dfs(1,1);
cout<<endl;
flag=false;
}
}
样例用时:
位运算优化,利用三个一维数组row[10],col[10],grid[10]的二进制来记录数字的占用情况。
可填的数字为1~9,故令init=111111111(2)=511=(1<<9)-1,表示一开始1~9都可以填(都未被占用),row[i]表示第i行数字的占用情况,col数组和grid数组同理。当需要获取一个x行y列z方块的空白格子能填的数字只需要先将row[x]&col[y]&grid[z](位与),再利用lowbit函数和预先处理好的figure数组依次取出能填的数字,如100100010即代表数字2、6、9可填。利用异或运算更新状态。
//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define e 2.71828182
#define Pi 3.141592654
using namespace std;
char table[10][10];
int row[10],col[10],grid[10];//二进制记录行、列、方块的数字占用情况
int figure[(1<<8)+1];//
bool flag=false;
int lowbit(int x)
{
return x&(-x);
}
void dfs(int x,int y)//填第(x,y)格
{
if(flag) return;//已找到一种解法
if(y>9) y=1,x+=1;
if(x>9)
{
for(int i=1;i<=9;i++)
cout<<table[i]+1;
flag=true;
return;
}
if(table[x][y]!='0') dfs(x,y+1);
else if(table[x][y]=='0')
{
int ord=(x-1)/3*3+(y-1)/3+1;//第几个方块
int status=row[x]&col[y]&grid[ord];//statue的二进制记录了行、列、方块数字的占用情况
for(int i=status;i>0;i-=lowbit(i))
{
int num=figure[lowbit(i)];//可填的数字
table[x][y]=num+'0';
row[x] ^= lowbit(i);
col[y] ^= lowbit(i);
grid[ord] ^= lowbit(i);
dfs(x,y+1);
//回溯
table[x][y]='0';
row[x] ^= lowbit(i);
col[y] ^= lowbit(i);
grid[ord] ^= lowbit(i);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string str;
for(int i=0;i<9;i++)
figure[1<<i]=i+1;
int init=(1<<9)-1;//二进制为111111111
while(cin>>str)
{
if(str=="end") break;
int count=0;
for(int i=1;i<=9;i++)
row[i]=col[i]=grid[i]=init;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
table[i][j]=str[count++];
if(table[i][j]=='.')
{
table[i][j]='0';
continue;
}
row[i] ^= (1<<(table[i][j]-'0'-1));
col[j] ^= (1<<(table[i][j]-'0'-1));
int ord=(i-1)/3*3+(j-1)/3+1;
grid[ord] ^= (1<<(table[i][j]-'0'-1));
}
/*clock_t t;
t=clock();*/
dfs(1,1);
/*t=clock()-t;
cout<<"时间为:"<<(((float)t)/CLOCKS_PER_SEC*1000)<<"ms\n";*/
cout<<endl;
flag=false;
}
}
样例用时:(由样例来看优化并不是特别明显?但是实际上位运算优化能提升较大的性能)
优化一下搜索顺序,优先处理可选数字较少的格子,一开始是静态的保存好,并未考虑到一个格子填好胡会对后续格子造成影响。
//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define e 2.71828182
#define Pi 3.141592654
using namespace std;
struct node
{
int x,y,z;//第x行,第y列,第z个方块
vector<int> able;//可填数字
friend bool operator < (node a,node b)
{
if(a.able.size()!=b.able.size()) return a.able.size()<b.able.size();
else if(a.x!=b.x) return a.x<b.x;
else return a.y<b.y;
/*if(a.x!=b.x) return a.x<b.x;
else return a.y<b.y;*/
}
}p[82];
char table[10][10];
int row[10][10],col[10][10],grid[10][10];
bool flag=false;
void dfs(int step,int sum)//填排好序的第step个空格,共有sum个空格
{
if(flag) return;//已找到一种解法
if(step>sum)
{
for(int i=1;i<=9;i++)
cout<<table[i]+1;
flag=true;
return;
}
for(int j=0;j<p[step].able.size()&&!flag;j++)
{
int i=p[step].able[j];
if(!row[p[step].x][i]&&!col[p[step].y][i]&&!grid[p[step].z][i])
{
table[p[step].x][p[step].y]=i+'0';
row[p[step].x][i]=1;col[p[step].y][i]=1;grid[p[step].z][i]=1;
dfs(step+1,sum);
//回溯
table[p[step].x][p[step].y]='0';
row[p[step].x][i]=0;col[p[step].y][i]=0;grid[p[step].z][i]=0;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string str;
while(cin>>str)
{
if(str=="end") break;
int count=0;
mem(row,0);mem(col,0);mem(grid,0);
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
table[i][j]=str[count++];
if(table[i][j]=='.') table[i][j]='0';
row[i][table[i][j]-'0']=1;
col[j][table[i][j]-'0']=1;
int ord=(i-1)/3*3+(j-1)/3+1;
grid[ord][table[i][j]-'0']=1;
}
count=0;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
if(table[i][j]!='0') continue;
count++;
p[count].able.clear();//血与泪的教训!!这个坑太深了
p[count].x=i,p[count].y=j;
int ord=(i-1)/3*3+(j-1)/3+1;
p[count].z=ord;
for(int k=1;k<=9;k++)
if(!row[i][k]&&!col[j][k]&&!grid[ord][k])
p[count].able.push_back(k);
}
sort(p+1,p+count+1);
/*clock_t t;
t=clock();*/
dfs(1,count);
/*t=clock()-t;
cout<<"时间为:"<<(((float)t)/CLOCKS_PER_SEC*1000)<<"ms\n";*/
cout<<endl;
flag=false;
}
}
当对node里<号的重载为
if(a.able.size()!=b.able.size()) return a.able.size()<b.able.size();
else if(a.x!=b.x) return a.x<b.x;
else return a.y<b.y;
时,即按可选数字数递增,若相同则按原顺序进行搜索,所需时间为:
当对node里<号的重载为
if(a.x!=b.x) return a.x<b.x;
else return a.y<b.y;
时,即按原顺序(数独中从上到下,从左到右)进行搜索,所需时间为:
很显然,这样做并不能满足要求。
后来改为动态查询,每次都查找可选数字最少的格子。但还是不幸超时
//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define e 2.71828182
#define Pi 3.141592654
#define P pair<int,int>
using namespace std;
char table[10][10];
int row[10],col[10],grid[10];//二进制记录行、列、方块的数字占用情况
int figure[(1<<8)+1];//
int lowbit(int x)
{
return x&(-x);
}
int z[10][10]={{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};
int num(int n)//返回可填数字个数
{
int ans=0;
for(int i=n;i;i-=lowbit(i))
ans++;
return ans;
}
P Min()//获得可填数字数最少的格子
{
int x=0,y=0,Min=1<<30;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(table[i][j]!='0') continue;
int status=row[i]&col[j]&grid[z[i][j]];
if(!status) return P(-1,-1);//该格子无可填的数字
if(num(status)<Min) Min=num(status),x=i,y=j;
}
}
return P(x,y);
}
bool dfs(int x,int y)//填第(x,y)格
{
int status=row[x]&col[y]&grid[z[x][y]];//statues的二进制记录了行、列、方块数字的占用情况
for(int i=status;i>0;i-=lowbit(i))
{
int num=figure[lowbit(i)];//可填的数字
table[x][y]=num+'0';
row[x] ^= lowbit(i);
col[y] ^= lowbit(i);
grid[z[x][y]] ^= lowbit(i);
P p=Min();
if(p.first==-1)
{
table[x][y]='0';
row[x] ^= lowbit(i);
col[y] ^= lowbit(i);
grid[z[x][y]] ^= lowbit(i);
continue;
}
if(p.first==0) return true;
if(dfs(p.first,p.second)) return true;
table[x][y]='0';
row[x] ^= lowbit(i);
col[y] ^= lowbit(i);
grid[z[x][y]] ^= lowbit(i);
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string str;
for(int i=0;i<9;i++)
figure[1<<i]=i+1;
int init=(1<<9)-1;//二进制为111111111
while(cin>>str)
{
if(str=="end") break;
int count=0;
for(int i=1;i<=9;i++)
row[i]=col[i]=grid[i]=init;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
table[i][j]=str[count++];
if(table[i][j]=='.')
{
table[i][j]='0';
continue;
}
row[i] ^= (1<<(table[i][j]-'0'-1));
col[j] ^= (1<<(table[i][j]-'0'-1));
grid[z[i][j]] ^= (1<<(table[i][j]-'0'-1));
}
/*clock_t t;
t=clock();*/
P p=Min();
dfs(p.first,p.second);
//t=clock()-t;
for(int i=1;i<=9;i++)
cout<<table[i]+1;
//cout<<"时间为:"<<(((float)t)/CLOCKS_PER_SEC*1000)<<"ms\n";
cout<<endl;
}
}
然后将各个10进制数字代表能填数字的个数保存在数组里,终于是过了
显然还有更进一步的优化,留在POJ3076去探索吧。
AC代码:
//CSDN博客:https://blog.csdn.net/qq_40889820
#include<iostream>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define random(a,b) (rand()%(b-a+1)+a)
#define e 2.71828182
#define Pi 3.141592654
#define P pair<int,int>
using namespace std;
char table[10][10];
int row[10],col[10],grid[10];//二进制记录行、列、方块的数字占用情况
int num[(1<<9)];//
int lowbit(int x)
{
return x&(-x);
}
int z[10][10]={{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,1,1,1,2,2,2,3,3,3},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,4,4,4,5,5,5,6,6,6},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9},
{0,7,7,7,8,8,8,9,9,9}};
int fun(int n)//返回可填数字个数
{
int ans=0;
for(int i=n;i;i-=lowbit(i))
ans++;
return ans;
}
P Min()//获得可填数字数最少的格子
{
int x=0,y=0,Min=1<<30;
for(int i=1;i<=9;i++)
{
for(int j=1;j<=9;j++)
{
if(table[i][j]!='0') continue;
int status=row[i]&col[j]&grid[z[i][j]];
if(!status) return P(-1,-1);//该格子无可填的数字
if(num[status]<Min) Min=num[status],x=i,y=j;
}
}
return P(x,y);
}
bool dfs(int x,int y)//填第(x,y)格
{
int status=row[x]&col[y]&grid[z[x][y]];//statues的二进制记录了行、列、方块数字的占用情况
for(int i=1;i<=9;i++)
{
if(status>>(i-1) & 1)//数字i未被占用
{
table[x][y]=i+'0';
row[x] ^= (1<< i-1);
col[y] ^= (1<< i-1);
grid[z[x][y]] ^= (1<< i-1);
P p=Min();
if(p.first==-1)
{
table[x][y]='0';
row[x] ^= (1<< i-1);
col[y] ^= (1<< i-1);
grid[z[x][y]] ^= (1<< i-1);
continue;
}
if(p.first==0) return true;
if(dfs(p.first,p.second)) return true;
table[x][y]='0';
row[x] ^= (1<< i-1);
col[y] ^= (1<< i-1);
grid[z[x][y]] ^= (1<< i-1);
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string str;
for(int i=0;i<(1<<9);i++)
num[i]=fun(i);
int init=(1<<9)-1;//二进制为111111111
while(cin>>str)
{
if(str=="end") break;
int count=0;
for(int i=1;i<=9;i++)
row[i]=col[i]=grid[i]=init;
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
{
table[i][j]=str[count++];
if(table[i][j]=='.')
{
table[i][j]='0';
continue;
}
row[i] ^= (1<<(table[i][j]-'0'-1));
col[j] ^= (1<<(table[i][j]-'0'-1));
grid[z[i][j]] ^= (1<<(table[i][j]-'0'-1));
}
/*clock_t t;
t=clock();*/
P p=Min();
dfs(p.first,p.second);
//t=clock()-t;
for(int i=1;i<=9;i++)
cout<<table[i]+1;
//cout<<"时间为:"<<(((float)t)/CLOCKS_PER_SEC*1000)<<"ms\n";
cout<<endl;
}
}
样例用时:(显然样例只能当样例看)
参考资料:
搜索_常规DFS_POJ3074_Sudoku
Sicily1317-Sudoku-位运算暴搜
二进制-高效位运算