题意:经典的数独游戏(3074),3076的范围更大,是16*16的方阵。
思路:Knuth大神提出的Dancing Links(DLX),具体可以参见他的论文。而对于Dancing Links的数据结构讲述的比较好的有(http://www.cnblogs.com/grenet/p/3145800.html,这篇博文的图画的非常仔细),而代码写的比较清晰的是(http://www.cnblogs.com/ylfdrib/archive/2010/10/06/1844785.html)。
3074:
#include <cstdio>
#include <cstring>
#include <cmath>
#define L 9
#define N L*L*L
#define M L*L*4
#define OFFSET L*L
#define INF 0x3fffffff
using namespace std;
struct node{
int l,r,u,d,t,x,y,c;
}p[N*4+M+5];
int cnt[M+5],top;
char tmp[L*L+5];
int res[L+2][L+2];
void init(){
memset(cnt, 0, sizeof(cnt));
for(int i = 0;i<=M;i++){
p[i].l = i-1;
p[i].r = i+1;
p[i].u = p[i].d = p[i].c = i;
}
p[0].l = M;
p[M].r = 0;
top = M;
}
int block(int i,int j){
int x = (int)sqrt((double)L);
return (i-1)/x*x + (j-1)/x;
}
void add(int l,int x,int y,int t,int c){
p[++top].x = x;
p[top].y = y;
p[top].t = t;
p[top].l = l;
p[p[top].l].r = top;
p[top].u = p[c].u;
p[p[top].u].d = top;
p[c].u = top;
p[top].d = p[top].c = c;
cnt[c]++;
}
void addp(int i,int j,int k){
add(top+4,i,j,k,(i-1)*L+j);
add(top, i, j, k, OFFSET+(i-1)*L+k);
add(top, i, j, k, OFFSET*2+(j-1)*L+k);
add(top, i, j, k, OFFSET*3+block(i,j)*L+k);
}
void del(int c){
int i,j;
p[p[c].l].r = p[c].r;
p[p[c].r].l = p[c].l;
for(i = p[c].d;i!=c;i=p[i].d)
for(j = p[i].r;j!=i;j=p[j].r){
p[p[j].u].d = p[j].d;
p[p[j].d].u = p[j].u;
cnt[p[j].c]--;
}
}
void re(int c){
int i,j;
p[p[c].l].r = c;
p[p[c].r].l = c;
for(i = p[c].d;i!=c;i=p[i].d)
for(j = p[i].r;j!=i;j=p[j].r){
p[p[j].u].d = j;
p[p[j].d].u = j;
cnt[p[j].c]++;
}
}
int dfs(){
int i,j,min = INF,c=0;
if(!p[0].r)
return 1;
for(i = p[0].r;i;i=p[i].r)
if(cnt[i] < min){
min = cnt[i];
c = i;
}
del(c);
for(i = p[c].d;i!=c;i=p[i].d){
res[p[i].x][p[i].y] = p[i].t;
for(j = p[i].r;j!=i;j=p[j].r)
del(p[j].c);
if(dfs())
return 1;
for(j = p[i].l;j!=i;j=p[j].l)
re(p[j].c);
}
re(c);
return 0;
}
int main(){
while(scanf("%s",tmp) && strcmp(tmp, "end")){
int i,j,k,now=-1;
init();
for(i = 1;i<=L;i++)
for(j = 1;j<=L;j++){
if(tmp[++now] == '.')
for(k = 1;k<=L;k++)
addp(i,j,k);
else
addp(i,j,tmp[now]-'0');
}
dfs();
for(i = 1;i<=L;i++)
for(j = 1;j<=L;j++)
printf("%d",res[i][j]);
putchar('\n');
}
return 0;
}
3076:
#include <cstdio>
#include <cstring>
#define N 16*16*16
#define M 16*16*4
#define OFFSET 16*16
#define INF 0x3fffffff
using namespace std;
char s[N+2][N+2];
int res[N+2][N+2];
struct node{ //t表示是数独中的第几个数[1..256],c表示列号
int l,r,u,d,x,y,c,t;
}p[N*4+M+5];
int top,cnt[M+2];
void init(){ //初始化第一行,也即列头链表
int i;
memset(cnt, 0, sizeof(cnt));
for(i = 0;i<=M;i++){
p[i].l = i-1;
p[i].r = i+1;
p[i].u = p[i].d = p[i].c = i;
}
p[M].r = 0;
p[0].l = M;
top = M;
}
int block(int i,int j){ //计算第i行第j列属于哪个快
return (i-1)/4*4+(j-1)/4;
}
void add(int l,int x,int y,int t,int c){//添加到DLX中
p[++top].l = l;
p[p[top].l].r = top;
p[top].x = x;
p[top].y = y;
p[top].t = t;
p[top].u = p[c].u;
p[p[top].u].d = top;
p[c].u = top;
p[top].d = p[top].c = c;
cnt[c]++;
}
void addp(int i,int j,int k){
int seq = (i-1)*16+j;
add(top+4,i,j,k,seq);
add(top, i, j, k, OFFSET+(i-1)*16+k); //第几行填入了k
add(top, i, j, k, OFFSET*2 + (j-1)*16+k); //第几列填入了k
add(top, i, j, k, OFFSET*3 + block(i,j)*16+k); //第几个正方形块填入了k
}
void del(int c){ //删除这一列(只是把这一列的头结点删除,然后将这一列有元素的行删去)
int i,j;
p[p[c].l].r = p[c].r;
p[p[c].r].l = p[c].l;
for(i = p[c].d;i!=c;i=p[i].d)
for(j = p[i].r;j!=i;j=p[j].r){
p[p[j].u].d = p[j].d;
p[p[j].d].u = p[j].u;
cnt[p[j].c]--;
}
}
void re(int c){ //恢复某一列
int i,j;
p[p[c].l].r = c;
p[p[c].r].l = c;
for(i = p[c].d;i!=c;i=p[i].d)
for(j = p[i].r;j!=i;j=p[j].r){
p[p[j].u].d = j;
p[p[j].d].u = j;
cnt[p[j].c]++;
}
}
int dfs(){ //深搜
int c=0,min = INF,i,j;
if(!p[0].r)
return 1;
for(i = p[0].r;i!=0;i=p[i].r) //这是一个优化,也就是选择列中元素最少的那一列先进行搜索
if(cnt[i] < min){
min = cnt[i];
c = i;
}
del(c);
for(i = p[c].d;i!=c;i=p[i].d){
res[p[i].x][p[i].y] = p[i].t;
for(j = p[i].r;j!=i;j=p[j].r)
del(p[j].c);
if(dfs())
return 1;
for(j = p[i].l;j!=i;j=p[j].l)
re(p[j].c);
}
re(c);
return 0;
}
int main(){
while(scanf("%s",s[0]) != EOF){
int i,j,k;
init();
for(i = 1;i<16;i++)
scanf("%s",s[i]);
for(i = 0;i<16;i++)//建立DLX链表
for(j = 0;j<16;j++){
if(s[i][j] == '-')
for(k = 1;k<=16;k++)//表明这个位置填1-16都有可能
addp(i+1,j+1,k);
else
addp(i+1,j+1,s[i][j]-'A'+1);
}
dfs();
for(i = 0;i<16;i++){
for(j = 0;j<16;j++)
putchar(res[i+1][j+1]-1+'A');
putchar('\n');
}
putchar('\n');
}
return 0;
}