Sudoku

文章介绍了如何使用C++编程解决数独问题,提供了两种不同的方法。第一种方法基于深度优先搜索,通过判断数字是否能填入特定位置来递归填充空白单元格。第二种方法同样利用深度优先搜索,通过维护行、列和3x3子网格的数字存在标志来检查有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务描述

数独是一个非常简单的游戏。如图所示,一张 99 列的正方形网格被分成933 小正方形网格。在一些方格中写着从 19 的数字,其他的方格为空。数独游戏是用从 19 的数字填入空的方格,每个方格填入一个数字,使得在每行、每列和每个 33 的小正方形网格中,所有从 19 的数字都会出现。请您编写一个程序来完成给出的数独游戏。

,

编程要求

根据提示,在右侧编辑器补充代码。

测试说明

输入说明

输入首先给出测试用例的数目。每个测试用例表示为 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值