上交三月月赛[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();
    }

}

 

 

posted @ 2013-03-12 11:47  ustc-acm  阅读(370)  评论(0编辑  收藏  举报