POJ 3074,3076,2676 数独 Dancing Links舞蹈链

POJ 3074 Sudoku

思路:数独正好是精确覆盖问题的最好例子,这个也算是舞蹈链的模板了吧。

这个和网络流一样,难的是建模。如果能想好怎么建立矩阵的话,那一切都容易了。

这题怎么建矩阵,可以看论文:http://www.cnblogs.com/steady/archive/2011/03/15/1984791.html

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffff
#define maxn 1000005
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int head,sz;
int U[maxn],D[maxn],L[maxn],R[maxn];//上下左右链表指针
int H[maxn],ROW[maxn],C[maxn],S[maxn],O[maxn];
void remove(int c)
{
    L[R[c]]=L[c];
    R[L[c]]=R[c];
    for(int i=D[c]; i!=c; i=D[i])
        for(int j=R[i]; j!=i; j=R[j])
        {
            U[D[j]]=U[j];
            D[U[j]]=D[j];
            --S[C[j]];
        }
}
void resume(int c)
{
    for(int i=U[c]; i!=c; i=U[i])
    {
        for(int j=L[i]; j!=i; j=L[j])
        {
            ++S[C[j]];
            U[D[j]]=j;
            D[U[j]]=j;
        }
    }
    L[R[c]]=c;
    R[L[c]]=c;
}
void init(int m)//m是列
{
    head=0;//头指针为0
    for(int i=0; i<=m; i++)
    {
        U[i]=i;
        D[i]=i;//建立双向十字链表
        L[i]=i-1;
        R[i]=i+1;
        S[i]=0;
    }
    R[m]=0;
    L[0]=m;
    S[0]=INF+1;
    sz=m+1;
    memset(H,0,sizeof(H));
}
void insert(int i, int j)
{
    if(H[i])
    {
        L[sz] = L[H[i]];
        R[sz] = H[i];
        L[R[sz]] = sz;
        R[L[sz]] = sz;
    }
    else
    {
        L[sz] = sz;
        R[sz] = sz;
        H[i] = sz;
    }
    U[sz] = U[j];
    D[sz] = j;
    U[D[sz]] = sz;
    D[U[sz]] = sz;
    C[sz] = j;
    ROW[sz] = i;
    ++S[j];
    ++sz;
}
bool dfs(int k,int len)
{
    if(R[head]==head)
    {
        sort(O,O+len*len);
        int p=0;
        for(int i=0; i<len; i++)
            for(int j=0; j<len; j++)
            {
                int num=O[p++];
                num=num-(i*len+j)*len;
                printf("%d",num);
            }
        puts("");
        return  true;
    }
    int s=INF,c;
    for (int t=R[head]; t!=head; t=R[t])
        if (S[t]<s) s=S[t],c=t;
    remove(c);
    for(int i=D[c]; i!=c; i=D[i])
    {
        O[k]=ROW[i];
        for(int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        if(dfs(k+1,len)) return  true;
        for(int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(c);
    return  false;
}
void calc(int i,int j,int k,int len)
{
    int r=(i*len+j-1)*len+k;
    int base=sqrt(len);
    //第i行有数字k
    insert(r,i*len+k);
    //第j列有数字k
    insert(r,len*len+(j-1)*len+k);
    //第k块有数字k
    int block=(j-1)/base*base+i/base;
    insert(r,len*len*2+block*len+k);
    //第i行j列有一个数字(限制一个出格子只填一个数)
    insert(r,len*len*3+i*len+j);
}
void build(char *s,int len)//len表示是几宫数独
{
    int i,j,k,p=0;
    init(len*len*4);
    for(i=0; i<len; i++)
        for(j=1; j<=len; j++,p++)
        {
            if(s[p]=='.')
                for(k=1; k<=len; k++)
                    calc(i,j,k,len);
            else calc(i,j,s[p]-'0',len);
        }
}
int main()
{
    //freopen("1.txt","r",stdin);
    char s[84];
    while(scanf("%s",s)&&strcmp(s,"end"))
    {
        build(s,9);
        dfs(0,9);//从根开始搜
    }
    return 0;
}

poj 3076 Sudoku

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffff
#define maxn 1000005
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int head,sz;
int U[maxn],D[maxn],L[maxn],R[maxn];//上下左右链表指针
int H[maxn],ROW[maxn],C[maxn],S[maxn],O[maxn];
void remove(int c)
{
    L[R[c]]=L[c];
    R[L[c]]=R[c];
    for(int i=D[c]; i!=c; i=D[i])
        for(int j=R[i]; j!=i; j=R[j])
        {
            U[D[j]]=U[j];
            D[U[j]]=D[j];
            --S[C[j]];
        }
}
void resume(int c)
{
    for(int i=U[c]; i!=c; i=U[i])
    {
        for(int j=L[i]; j!=i; j=L[j])
        {
            ++S[C[j]];
            U[D[j]]=j;
            D[U[j]]=j;
        }
    }
    L[R[c]]=c;
    R[L[c]]=c;
}
void init(int m)//m是列
{
    head=0;//头指针为0
    for(int i=0; i<=m; i++)
    {
        U[i]=i;
        D[i]=i;//建立双向十字链表
        L[i]=i-1;
        R[i]=i+1;
        S[i]=0;
    }
    R[m]=0;
    L[0]=m;
    S[0]=INF+1;
    sz=m+1;
    memset(H,0,sizeof(H));
}
void insert(int i, int j)
{
    if(H[i])
    {
        L[sz] = L[H[i]];
        R[sz] = H[i];
        L[R[sz]] = sz;
        R[L[sz]] = sz;
    }
    else
    {
        L[sz] = sz;
        R[sz] = sz;
        H[i] = sz;
    }
    U[sz] = U[j];
    D[sz] = j;
    U[D[sz]] = sz;
    D[U[sz]] = sz;
    C[sz] = j;
    ROW[sz] = i;
    ++S[j];
    ++sz;
}
bool dfs(int k,int len)
{
    if(R[head]==head)
    {
        sort(O,O+len*len);
        int p=0;
        for(int i=0; i<len; i++)
        {
            for(int j=0; j<len; j++)
            {
                int num=O[p++];
                num=num-(i*len+j)*len;
                printf("%c",num+'A'-1);
            }
            puts("");
        }
        puts("");
        return  true;
    }
    int s=INF,c;
    for (int t=R[head]; t!=head; t=R[t])
        if (S[t]<s) s=S[t],c=t;
    remove(c);
    for(int i=D[c]; i!=c; i=D[i])
    {
        O[k]=ROW[i];
        for(int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        if(dfs(k+1,len))
            return  true;
        for(int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(c);
    return  false;
}
void calc(int i,int j,int k,int len)
{
    int r=(i*len+j-1)*len+k;
    int base=sqrt(len);
    //第i行有数字k
    insert(r,i*len+k);
    //第j列有数字k
    insert(r,len*len+(j-1)*len+k);
    //第k块有数字k
    int block=(j-1)/base*base+i/base;
    insert(r,len*len*2+block*len+k);
    //第i行j列有一个数字(限制一个出格子只填一个数)
    insert(r,len*len*3+i*len+j);
}
void build(char s[][17],int len)//len表示是几宫数独
{
    int i,j,k;
    init(len*len*4);
    for(i=0; i<len; i++)
        for(j=1; j<=len; j++)
        {
            if(s[i][j-1]=='-')
                for(k=1; k<=len; k++)
                    calc(i,j,k,len);
            else calc(i,j,s[i][j-1]-'A'+1,len);
        }
}
int main()
{
    //freopen("1.txt","r",stdin);
    char s[17][17];
    while(~scanf("%s",s[0]))
    {
        for(int i=1; i<16; i++)
            scanf("%s",s[i]);
        build(s,16);
        dfs(0,16);//从根开始搜
    }
    return 0;
}

POJ 2676 Sudoku

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffff
#define maxn 1000005
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int head,sz;
int U[maxn],D[maxn],L[maxn],R[maxn];//上下左右链表指针
int H[maxn],ROW[maxn],C[maxn],S[maxn],O[maxn];
void remove(int c)
{
    L[R[c]]=L[c];
    R[L[c]]=R[c];
    for(int i=D[c]; i!=c; i=D[i])
        for(int j=R[i]; j!=i; j=R[j])
        {
            U[D[j]]=U[j];
            D[U[j]]=D[j];
            --S[C[j]];
        }
}
void resume(int c)
{
    for(int i=U[c]; i!=c; i=U[i])
    {
        for(int j=L[i]; j!=i; j=L[j])
        {
            ++S[C[j]];
            U[D[j]]=j;
            D[U[j]]=j;
        }
    }
    L[R[c]]=c;
    R[L[c]]=c;
}
void init(int m)//m是列
{
    head=0;//头指针为0
    for(int i=0; i<=m; i++)
    {
        U[i]=i;
        D[i]=i;//建立双向十字链表
        L[i]=i-1;
        R[i]=i+1;
        S[i]=0;
    }
    R[m]=0;
    L[0]=m;
    S[0]=INF+1;
    sz=m+1;
    memset(H,0,sizeof(H));
}
void insert(int i, int j)
{
    if(H[i])
    {
        L[sz] = L[H[i]];
        R[sz] = H[i];
        L[R[sz]] = sz;
        R[L[sz]] = sz;
    }
    else
    {
        L[sz] = sz;
        R[sz] = sz;
        H[i] = sz;
    }
    U[sz] = U[j];
    D[sz] = j;
    U[D[sz]] = sz;
    D[U[sz]] = sz;
    C[sz] = j;
    ROW[sz] = i;
    ++S[j];
    ++sz;
}
bool dfs(int k,int len)
{
    if(R[head]==head)
    {
        sort(O,O+len*len);
        int p=0;
        for(int i=0; i<len; i++)
        {
            for(int j=0; j<len; j++)
            {
                int num=O[p++];
                num=num-(i*len+j)*len;
                printf("%d",num);
            }
            puts("");
        }
        return  true;
    }
    int s=INF,c;
    for (int t=R[head]; t!=head; t=R[t])
        if (S[t]<s) s=S[t],c=t;
    remove(c);
    for(int i=D[c]; i!=c; i=D[i])
    {
        O[k]=ROW[i];
        for(int j=R[i]; j!=i; j=R[j])
            remove(C[j]);
        if(dfs(k+1,len))
            return  true;
        for(int j=L[i]; j!=i; j=L[j])
            resume(C[j]);
    }
    resume(c);
    return  false;
}
void calc(int i,int j,int k,int len)
{
    int r=(i*len+j-1)*len+k;
    int base=sqrt(len);
    //第i行有数字k
    insert(r,i*len+k);
    //第j列有数字k
    insert(r,len*len+(j-1)*len+k);
    //第k块有数字k
    int block=(j-1)/base*base+i/base;
    insert(r,len*len*2+block*len+k);
    //第i行j列有一个数字(限制一个出格子只填一个数)
    insert(r,len*len*3+i*len+j);
}
void build(char s[][10],int len)//len表示是几宫数独
{
    int i,j,k;
    init(len*len*4);
    for(i=0; i<len; i++)
        for(j=1; j<=len; j++)
        {
            if(s[i][j-1]=='0')
                for(k=1; k<=len; k++)
                    calc(i,j,k,len);
            else calc(i,j,s[i][j-1]-'0',len);
        }
}
int main()
{
    //freopen("1.txt","r",stdin);
    int t;
    cin>>t;
    while(t--)
    {
        char s[10][10];
        for(int i=0; i<9; i++)
            scanf("%s",s[i]);
        build(s,9);
        dfs(0,9);//从根开始搜
    }
    return 0;
}

你可能感兴趣的:(POJ 3074,3076,2676 数独 Dancing Links舞蹈链)