题意:经典的数独游戏(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; }