poj 3074/3076 数独(Dancing Links)

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


你可能感兴趣的:(poj 3074/3076 数独(Dancing Links))