任务描述
数独是一个非常简单的游戏。如图所示,一张 9
行 9
列的正方形网格被分成9
个 33
小正方形网格。在一些方格中写着从 1
到 9
的数字,其他的方格为空。数独游戏是用从 1
到 9
的数字填入空的方格,每个方格填入一个数字,使得在每行、每列和每个 33
的小正方形网格中,所有从 1
到 9
的数字都会出现。请您编写一个程序来完成给出的数独游戏。
编程要求
根据提示,在右侧编辑器补充代码。
测试说明
输入说明
输入首先给出测试用例的数目。每个测试用例表示为 9
行,相应于网格的行;在每一行上给出一个 9
位数字的字符串,对应于这一行中的方格;如果方格为空,则用 0
表示。
输出说明
对于每个测试用例,程序以与输入数据相同的格式输出解决方案。空的方格要按照规则填入数字。如果解决方案不是唯一的,那么程序可以输出其中任何一个。
平台会对你编写的代码进行测试:
测试输入: 1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107
预期输出: 143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127
试题来源:ACM Southeastern Europe 2005
方法1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include<iomanip>
#include <queue>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
typedef unsigned long long ULL;
const int N = 10;
const double PI = acos(-1);
int num,flag;
int mp[N][N]; //地图
char str[N];
struct point
{
int x;
int y;
}node[100];
void print()
{
for(int i=0;i<9;i++)
{
for(int j=0;j<9;j++)
{
cout<<mp[i][j];
}
cout<<endl;
}
}
bool judge(int t,int k) // t是要判断是数字 k是要判断那个点
{
int a,b;
//确定k个点的时候 就要去确定点的 列 和 行的 情况
for(int i=0;i<9;i++)
{
if(mp[node[k].x][i] == t || mp[i][node[k].y] == t)
{
return 0;
}
}
//寻找小方格在 3 * 3方格中的起点
a = (node[k].x / 3) * 3;
b = (node[k].y / 3) * 3;
for(int i=a;i<a+3;i++)
{
for(int j=b;j<b+3;j++)
{
if(mp[i][j] == t)
return 0;
}
}
return 1;
}
void dfs(int k)
{
if(k == num) //说明前num-1个点已经选完了
{
flag = 1;
print();
return ;
}
else
{
for(int i=1;i<=9;i++)
{
if(judge(i,k) && !flag)
{
//如果可以匹配成功
mp[node[k].x][node[k].y] = i;
dfs(k+1);
//如果失败回溯的时候要恢复
mp[node[k].x][node[k].y] = 0;
}
}
}
}
void solve()
{
num = 0;
for(int i=0;i<9;i++)
{
cin>>str;
for(int j=0;j<9;j++)
{
//0的点是需要我们填的 单独用struct把他拿出来 不需要的就先放在 mp里面
if( str[j] == '0' )
{
node[num].x = i;
node[num++].y = j;
mp[i][j] = 0;
}
else
{
mp[i][j] = str[j] - '0';
}
}
}
flag = 0;
dfs(0);
}
signed main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T = 1;
cin>>T;
while(T--)
solve();
}
方法2
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define maxn 102
int hn[10][10]; //第i行中数字t存在的标志为hn[i][t]
int ln[10][10]; //第j列中数字t存在的标志为ln[j][t]
int gn[10][10]; //子网格n存在数字t的标志为gn[n][t]
int map[10][10]; //数独网格
struct pos{ //赋值行号r与列号c
int r,c;
pos(int rr,int cc):r(rr),c(cc){}
};
vector<pos> b; //容器b存储所有空格的行列位置
inline int gb(int r,int c){ //内联函数gb:计算和返回(r, c)对应的子网格序号
int rr=r/3;
int cc=c/3;
return rr*3+cc;
}
void saf(int i,int j,int num,int f){ //设定i行和j列存在数字num的标志f
hn[i][num]=f; //设置第i行存在数字num的标志f
ln[j][num]=f; //设置第j列存在数字num的标志f
gn[gb(i,j)][num]=f; //设置对应子网格gb(i, j)存在数字num的标志f
}
bool isk(int i,int j,int num){ //计算和返回数字num可否置入(i, j)的标志
return !gn[gb(i,j)][num] && !hn[i][num] && !ln[j][num];
}
int dfs(int n){ //从位置n出发,计算和返回数独方案的可行性
if(n<0) return 1; //若所有空格被填入数字,则返回成功标志
int r=b[n].r; //计算第n个空格对应的(r, c)
int c=b[n].c;
for(int i=1;i<=9;++i){ //枚举每个数字
if(isk(r,c,i)){ //若数字i可置入(r, c),则该位置放入i
map[r][c]=i;
saf(r,c,i,1); //设定r行和c列存在数字i的标志
//若可按规则将数字填入剩余空格,则返回成功标志;否则回溯,恢复填前状态(r行和c列不存在数字i)
if(dfs(n-1)) return 1;
else saf(r,c,i,0);
}
}
return 0;
}
int main()
{
int t;
cin>>t; //输入测试用例数
while(t--){ //依次处理每个测试用例-
memset(hn,0,sizeof(hn)); //每行和每列存在数字的标志初始化为0
memset(ln,0,sizeof(ln));
memset(gn,0,sizeof(gn)); //每个位置存在数字的标志初始化为0
b.clear(); //空格位置初始化为空
for(int i=0;i<9;i++) //自上而下、从左而右枚举每个位置
for(int j=0;j<9;j++){
char c;
cin>>c; //输入(i, j)的数符c
map[i][j]=c-'0'; //将c对应的数字存入(i, j)
//若(i, j)非空格,则设定i行和j列存该数字;否则将(i, j)存入容器b
if(map[i][j]) saf(i,j,map[i][j],1);
else b.push_back(pos(i,j));
}
if(dfs(b.size()-1)){ //若可按规则将数字填满空格,则输出解决方案
for(int i=0;i<9;i++){ //自上而下输出各行信息
for(int j=0;j<9;j++) cout<<char(map[i][j]+'0'); //输出i行的9个数符
cout<<endl; //回车
}
}
}
return 0;
}