CQOI2013 NKOJ 2194 新数独
题目描述
下面是一个没有数字,只有大小关系(没错!那些尖角都是“大于”符号!)的数独:
除了大小关系外(注意相邻格子不能相同),还需要满足通常的数独规则:
每个格子都是1~9 的数字
每行都是1~9的排列
每列都是1~9的排列
每个3*3的子矩阵(上图中用粗线隔开,一共有3*3个这样的子矩阵)都是1~9的排列
为了美观,每个3*3子矩阵的所有12对相邻格子的大小关系都将给出。
输入格式
输入一共15行,包含一个新数独的实例。第奇数行包含左右方向的符号(<和>),第偶数行包含上下方向的符号(^和v)。
输出格式
输出包含9行,每行9个1~9的数字,以单个空格隔开。输入保证解惟一。
样例输入
> < < < > <
v ^ v v ^ v ^ ^ v
< < < > < <
v ^ v ^ v v ^ ^ v
< < < < > >
< > > > < >
v v ^ ^ v ^ ^ v v
< > > < > >
^ v v v ^ v v ^ v
> < < > > >
< > > > > <
v v v v ^ ^ ^ ^ ^
> < < < < <
^ ^ ^ ^ ^ v v v ^
> > < > < <
样例输出
5 3 9 4 6 8 2 1 7
2 4 8 1 9 7 3 5 6
1 6 7 2 3 5 9 8 4
6 8 1 7 4 2 5 9 3
3 7 5 9 1 6 8 4 2
9 2 4 5 8 3 7 6 1
7 9 6 8 2 1 4 3 5
4 1 2 3 5 9 6 7 8
8 5 3 6 7 4 1 2 9
这道题处理输入其实比较恶心,由于markdown的格式问题体现不出来。
其实是一道比较水的搜索,但是由于自己太菜了还是写一写。
处理剪枝的问题时,不要每遇到一个状态就检查已经填好的所有数是否满足所有约束条件。可以发现,只需要每填一个数就判断一下它所在行是否满足条件、所在列是否满足条件、所在九宫格是否满足条件、上边和左边是否满足大小关系(从上到下,从左到右填数)即可。
此外,在当前状态判断出能转移到的合法状态,而不是转移到了状态再判断。因为大小关系实际上是较强的约束条件,先判断合法状态可以减掉很大部分枝,而转移后再判断就没有这个优势。
代码:
#include<stdio.h>
#include<cstring>
int id[105][105],rel[105][105],X[105],Y[105],Map[105][105],be[15][15];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
bool R[15][15],C[15][15],B[15][15];
bool Find;
void DFS(int cur)
{
if(Find)return;
int i,j,x,y,a,b,k,tx,ty,tmp,ida,idb;
bool mark[10];//标记哪些数字一定不能填
if(cur==82)
{
Find=true;
for(i=1;i<=9;i++,putchar('\n'))
for(j=1;j<=9;j++)printf("%d ",Map[i][j]);
return;
}
memset(mark,false,sizeof(mark));
x=X[cur];y=Y[cur];a=be[x][y];
for(i=1;i<=9;i++)if(R[x][i]||C[y][i]||B[a][i])mark[i]=true;//已经出现过,不能填
ida=id[x][y];
for(k=0;k<4;k++)
{
tx=x+dx[k];ty=y+dy[k];
if(tx&&ty&&tx<=9&&ty<=9)
{
if(id[tx][ty]>cur)continue;
idb=id[tx][ty];
if(rel[ida][idb]==0)continue;
if(rel[ida][idb]==1)for(i=1;i<=Map[tx][ty];i++)mark[i]=true;
else for(i=Map[tx][ty];i<=9;i++)mark[i]=true;//约束条件强剪枝
}
}
for(i=1;i<=9;i++)
{
if(mark[i])continue;
Map[x][y]=i;R[x][i]=true;C[y][i]=true;B[a][i]=true;
DFS(cur+1);
R[x][i]=false;C[y][i]=false;B[a][i]=false;
}
}
int main()
{
int i,j,k,l,cnt;
char c,s[6][20];
for(cnt=0,i=1;i<=9;i++)
for(j=1;j<=9;j++)id[i][j]=++cnt,X[cnt]=i,Y[cnt]=j;
for(k=1;k<=3;k++)
for(i=1;i<=5;i++)
{
for(j=1;j<=18;j++)s[i][j]=getchar();
for(j=1;j<=18;j++)
{
int x,y,tx,ty,ida,idb;
if(s[i][j]=='>')
{
x=(k-1)*3+(i+1)/2;y=(j+1)/2;
tx=x;ty=y+1;
ida=id[x][y];idb=id[tx][ty];
rel[ida][idb]=1;
rel[idb][ida]=-1;
}
if(s[i][j]=='<')
{
x=(k-1)*3+(i+1)/2;y=(j+1)/2;
tx=x;ty=y+1;
ida=id[x][y];idb=id[tx][ty];
rel[ida][idb]=-1;
rel[idb][ida]=1;
}
if(s[i][j]=='^')
{
x=(k-1)*3+(i+1)/2;y=(j+1)/2;
tx=x+1;ty=y;
ida=id[x][y];idb=id[tx][ty];
rel[ida][idb]=-1;
rel[idb][ida]=1;
}
if(s[i][j]=='v')
{
x=(k-1)*3+(i+1)/2;y=(j+1)/2;
tx=x+1;ty=y;
ida=id[x][y];idb=id[tx][ty];
rel[ida][idb]=1;
rel[idb][ida]=-1;
}
}
}//处理输入,写得渣。事实上完全可以不开rel数组
for(i=1;i<=3;i++)
for(j=1;j<=3;j++)
{
int x,y,tmp;
x=(i-1)*3+1;y=(j-1)*3+1;tmp=(i-1)*3+j;
for(k=0;k<3;k++)
for(l=0;l<3;l++)
{
be[x+k][y+l]=tmp;
}
}//be记录处在的九宫格的编号
DFS(1);
}