题目
下面是一个没有数字,只有大小关系(没错!那些尖角都是“大于”符号!)的数独:
除了大小关系外(注意相邻格子不能相同),还需要满足通常的数独规则:
l 每个格子都是1~9 的数字
l 每行都是1~9的排列
l 每列都是1~9的排列
l 每个3*3的子矩阵(上图中用粗线隔开,一共有3*3个这样的子矩阵)都是1~9的排列
为了美观,每个3*3子矩阵的所有12对相邻格子的大小关系都将给出。
这道题,我们可以注意到有朴素数独的限制,例如每行都是1~9的排列
, 每列都是1~9的排列,但光有这个,搜索还是很慢。
这里,我打算所以一下一开始做题时我对这道题的看法:
那些大于小于号,假设我们现在要确定9要放哪里,那么画出来的图,对应9的位置,所有符号都是它指向外面的(就是它周围的都是他指向外面的大于号),这简直是9在大吼着:“我在这里…”…于是我就顺从了..
对于这个发现,我们可以从大到小枚举先放哪些数,放完9个位置后再放其他的,若存在a<b,则b连a一条边,并将a入度+1。由于我们每次放的都是现存最大的数,那么我们按小九宫格为单位,每个九宫格里找到入度为0的位置,将数填进去,然后消除该位置对周围位置的影响(类似拓扑排序,将它连出去的位置入度-1)。
加上这个优化,我们就可以飞快的跑过了….(说实话,刚提交代码,连running都没有看到..)
好了,我们来讨论一下为甚么这个剪枝这么强力?
由于保证存在唯一解,对于相同决策,即同一个数(指一个小九宫格内)能填的位置(入度为0)的地方不会超过5,而当你选择了一个位置填入的时候,说明你周围的位置都是先前不可能决策到的(这个可以证明前者),而当前存在多个决策时,则说明以后的决策会少那么多(我们可以想着一个正向决策即由大取到小,一个反向决策即由小取到大)【而且加上数独的限制,均摊相同决策不超过3个】均摊下来,若要跑得最慢的话,最大决策层在中间,只会有3个相同决策,其余两端决策数会依次递减,均摊 69 (我估计的,这也可以接受)而实际上非常小,通常由于唯一解,第一次就会直接搜到答案。
贴代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 10
using namespace std;
int in[82],g[82],a[1000][2],d[82],help[N],b[N];
bool bzh[9][9],bzz[9][9];
void ins(int x,int y){
static int sum=0;
a[++sum][0]=y,a[sum][1]=g[x],g[x]=sum;
}
void did1(int x){
static char c;
for (int j=1;j<=3;j++){
for (int k=1;k<=2;k++){
scanf(" %c",&c);
if (c=='>')
in[x+1]++,ins(x,x+1);
else
in[x]++,ins(x+1,x);
x++;
}
x++;
}
}
void did2(int x){
static char c;
for (int j=1;j<=3;j++)
for (int k=1;k<=3;k++){
scanf(" %c",&c);
if (c=='v')
in[x]++,ins(x-9,x);
else
in[x-9]++,ins(x,x-9);
x++;
}
}
void init(){
static int x;
for (int i=1;i<=3;i++){
x=(i-1)*27+1;
did1(x);
x+=9;
did2(x);
did1(x);
x+=9;
did2(x);
did1(x);
}
}
int did(int x){
return !x?9:x;
}
bool jian(int x,int y){
return !bzz[x][did(y%9)]&&!bzh[x][y/9+(y%9>0)];
}
void change(int x,int y){
bzz[x][did(y%9)]=bzh[x][y/9+(y%9>0)]=1;
for (int i=g[y];i;i=a[i][1])
in[a[i][0]]--;
}
void changeback(int x,int y){
bzz[x][did(y%9)]=bzh[x][y/9+(y%9>0)]=0;
for (int i=g[y];i;i=a[i][1])
in[a[i][0]]++;
}
bool dfs(int x,int y){
int s;
if (x>9){
x=1,y--;
}
if (!y)return 1;
s=help[x];
++x;
for (int i=1;i<=9;i++)
if (!d[s+b[i]]&&jian(y,s+b[i])&&!in[s+b[i]]){
d[s+b[i]]=y;
change(y,s+b[i]);
if (dfs(x,y))return 1;
changeback(y,s+b[i]);
d[s+b[i]]=0;
}
return 0;
}
void work(){
help[1]=1,help[2]=4,help[3]=7,help[4]=28,help[5]=31,help[6]=34,help[7]=55,help[8]=58,help[9]=61;
b[2]=1,b[3]=2,b[4]=9,b[5]=10,b[6]=11,b[7]=18,b[8]=19,b[9]=20;
dfs(1,9);
}
void write(){
for (int i=1;i<=81;i++){
if (!(i%9))printf("%d\n",d[i]);
else
printf("%d ",d[i]);
}
}
int main(){
init();
work();
write();
return 0;
}