上交三月月赛[SJTU] 1106 sudoku

题目大意:给出一道数独题,判断该数独是否有解且有唯一解。

解题思路:

  由前几题的难度得,此题的难度不会太过分,所以简单暴力就可以了,40ms用时一本满足。

  简单地讲一下具体的实现,从左上开始从左到右从上到下一格一格枚举过去,枚举当前格子的数字时,判断一下这个数字能不能用就行了(判断是否在该行,该列,该区域出现过),全部填满时就找到了一个解,找到第二个解的时候停止搜索即可。

  关于判断某个数字在当前区域是否用过,比较方便的方法是,事先算好p[i][j],用来记录第i行第j格属于哪一列,哪一行,哪一区域,这样在写dfs时会方便很多。

代码:

/*

被注释掉的代码都是用来调试的,取消注释后可以用来查看程序运行过程以便调试。

*/



#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cmath>

#include <cstring>

using namespace std;

#define FOR(i,a,b) for(int i=a;i<=b;i++)

#define MST(a,b) memset(a,b,sizeof(a))

#define MAXL 10

int error;//用于发现不合理时终止程序

int g[MAXL][MAXL];//存放数组初始状态

int p1[MAXL][MAXL],p2[MAXL][MAXL],p3[MAXL][MAXL];//分别记录(i,j)所对应的行,列,区域

int b1[MAXL][MAXL],b2[MAXL][MAXL],b3[MAXL][MAXL];//记录编号为i的行,列,区域的数字使用情况

void init()//读入部分

{

    error=0;

    FOR(i,1,9)FOR(j,1,9)scanf("%d",&g[i][j]);

    MST(b1,0);

    MST(b2,0);

    MST(b3,0);

    FOR(i,1,9)FOR(j,1,9)if(g[i][j])

    {

        int p=p1[i][j],x=g[i][j];

        if(b1[p][x])error=1;

        b1[p][x]=1;

        p=p2[i][j];

        if(b2[p][x])error=1;

        b2[p][x]=1;

        p=p3[i][j];

        if(b3[p][x])error=1;

        b3[p][x]=1;

    }

}

int g2[MAXL][MAXL];//调试时用的数组,可无视

int tot;//记录解的个数

void dfs(int x,int y)

{

    if(x==10)//dfs到第10行意味着已经全部填满

    {

        tot++;

        if(tot>1)error=1;//若发现第二个解则终止程序

    /*FOR(i,1,9)

    {

        FOR(j,1,9)printf("%d ",g2[i][j]+g[i][j]);

        printf("\n");

    }

    printf("\n");*/

        return;

    }

    int xt=x,yt=y+1;//计算下一个

    if(yt>9){xt++;yt=1;}

    if(g[x][y]){dfs(xt,yt);return;}//若该格已有数字则无需尝试,直接进入下一格

    int c1=p1[x][y],c2=p2[x][y],c3=p3[x][y];//记录当前格所在的行,列,区域的编号

    FOR(i,1,9)

    if(b1[c1][i]+b2[c2][i]+b3[c3][i]==0)//是否在当前行,列,区域都未使用

    {

        b1[c1][i]=1;

        b2[c2][i]=1;

        b3[c3][i]=1;

        //g2[x][y]=i;

        dfs(xt,yt);//进入下一格继续尝试

        //g2[x][y]=0;

        if(error)return;

        b1[c1][i]=0;

        b2[c2][i]=0;

        b3[c3][i]=0;

    }

}

void work()//计算部分g2和输出error什么的请无视

{

    MST(g2,0);

    tot=0;

    dfs(1,1);

    //if(error)printf("error");

    if(tot==0 || error)printf("No\n");

    else printf("Yes\n");

}

int main()

{

    freopen("in.txt","r",stdin);//文件输入以便调试

    //freopen("out.txt","w",stdout);

    FOR(i,1,9)FOR(j,1,9)p1[i][j]=i;

    FOR(i,1,9)FOR(j,1,9)p2[i][j]=j;

    FOR(i,1,9)FOR(j,1,9)p3[i][j]=((i-1)/3)*3+((j-1)/3)+1;//预处理i,j所在区域

    /*FOR(i,1,9)

    {

        FOR(j,1,9)printf("%d ",p3[i][j]);

        printf("\n");

    }*/

    //上面的代码用来检查预处理时有无算错,下面开始是正片

    int nn;

    scanf("%d",&nn);

    FOR(ii,1,nn)

    {

        init();

        if (error){printf("No\n");continue;}

        work();

    }



}

 

 

你可能感兴趣的:(sudo)