HDOJ1426-DLX解sudoku

这是我做的第一个DLX解数独的题,建好模型就是解精确覆盖问题了,也不是很难,我不知道别人是怎么处理数独中原始的数据的,我的做法是先不管原有的的数建个729行*324列的十字双向链表,再处理掉原有的数字,再dance填空;

郁闷的是由于未知的错误我查了好几遍,处理原有数据的方法也变了好几变,查不出来后只好重写dance部分才行~等下再比比看看到底出问题在哪儿了。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int N=729;
const int M=324;
const int MM=5000;
const int pl[9][9]={{0,0,0,1,1,1,2,2,2},
                    {0,0,0,1,1,1,2,2,2},
                    {0,0,0,1,1,1,2,2,2},
                    {3,3,3,4,4,4,5,5,5},
                    {3,3,3,4,4,4,5,5,5},
                    {3,3,3,4,4,4,5,5,5},
                    {6,6,6,7,7,7,8,8,8},
                    {6,6,6,7,7,7,8,8,8},
                    {6,6,6,7,7,7,8,8,8}};

int cnt,a[9][9];
int S[MM],V[MM],flag[MM],L[MM],R[MM],U[MM],D[MM],C[MM],H[MM];

void add(int i,int j)
{
    cnt++;
    C[cnt]=j;
    H[cnt]=i;
    S[j]++;

    D[cnt]=j;
    U[cnt]=U[j];
    if (V[i]) R[cnt]=V[i],L[cnt]=L[V[i]];
    else      R[cnt]=L[cnt]=cnt;
    V[i]=cnt;

    U[D[cnt]]=cnt;
    D[U[cnt]]=cnt;
    R[L[cnt]]=cnt;
    L[R[cnt]]=cnt;
}

void build_graph()
{
    int i,j,k,x;

    cnt=M;
    for (i=0; i<=cnt; i++)
    {
        flag[i]=S[i]=0;
        R[i]=i+1;
        L[i+1]=i;
        C[i]=U[i]=D[i]=i;
    }
    L[0]=cnt; R[cnt]=0;

    for (i=0; i<=N; i++) V[i]=0;

    for (i=0; i<9; i++)
      for (j=0; j<9; j++)
        for (k=0; k<9; k++)
        {
            x=i*81+j*9+k;
            add(x,i*9+k+1);
            add(x,j*9+k+82);
            add(x,pl[i][j]*9+k+163);
            add(x,i*9+j+244);
        }
}

void remove(int c)
{
    R[L[c]]=R[c];
    L[R[c]]=L[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])
        {
            D[U[j]]=j;
            U[D[j]]=j;
            S[C[j]]++;
        }
    L[R[c]]=R[L[c]]=c;
}

void pre_dance()
{
    for (int i=0; i<9; i++)
      for (int j=0; j<9; j++) if (a[i][j]!=-1)
      {
          flag[i*9+a[i][j]+1]=1;
          flag[j*9+a[i][j]+82]=1;
          flag[pl[i][j]*9+a[i][j]+163]=1;
          flag[i*9+j+244]=1;
      }
    for (int i=1; i<=M; i++) if (flag[i]) remove(i);
}

bool dance()
{
    if (!R[0]) return true;

    int s(N),c;
    for (int i=R[0]; i && s>1; i=R[i]) if (S[i]<s) s=S[i],c=i;

    remove(c);
    for (int i=D[c]; i!=c; i=D[i])
    {
        a[H[i]/81][(H[i]/9)%9]=H[i]%9;
        for (int j=R[i]; j!=i; j=R[j]) remove(C[j]);
        if (dance()) return true;
        for (int j=L[i]; j!=i; j=L[j]) resume(C[j]);
    }
    resume(c);

    return false;
}

inline int str2digit(char *s)
{
    if (s[0]=='?') return -1;
    else           return s[0]-'1';
}

int main()
{
    int i,j,blank=0;
    char s[4];
    while (scanf("%s",s)!=EOF)
    {
        if (blank) printf("\n");
        else       blank=1;
        a[0][0]=str2digit(s);
        for (j=1; j<9; j++)
        {
            scanf("%s",s);
            a[0][j]=str2digit(s);
        }
        for (i=1; i<9; i++)
          for (j=0; j<9; j++)
          {
              scanf("%s",s);
              a[i][j]=str2digit(s);
          }

        build_graph();
        pre_dance();
        dance();
        for (i=0; i<9; i++)
        {
            for (j=0; j<8; j++) printf("%d ",a[i][j]+1);
            printf("%d\n",a[i][8]+1);
        }
    }
    return 0;
}

偶又换了种预处理原有数据的方式,没有pre_dance了,直接在建图时解决,快一点了,而且改下K和KK值,以下代码能直接运用到16*16的数独(POJ3076)当中去,不过时间有点慢,有待优化~

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int N=729;
const int M=324;
const int K=9;
const int KK=81;
const int MM=5000;
const int pl[K][K]={{0,0,0,1,1,1,2,2,2},
                    {0,0,0,1,1,1,2,2,2},
                    {0,0,0,1,1,1,2,2,2},
                    {3,3,3,4,4,4,5,5,5},
                    {3,3,3,4,4,4,5,5,5},
                    {3,3,3,4,4,4,5,5,5},
                    {6,6,6,7,7,7,8,8,8},
                    {6,6,6,7,7,7,8,8,8},
                    {6,6,6,7,7,7,8,8,8}};

int cnt,a[K][K];
int S[MM],V[MM],flag[MM],L[MM],R[MM],U[MM],D[MM],C[MM],H[MM];

inline void add(int i,int j)
{
    if (flag[j]) return;
    cnt++;
    C[cnt]=j;
    H[cnt]=i;
    S[j]++;

    D[cnt]=j;
    U[cnt]=U[j];
    if (V[i]) L[cnt]=V[i],R[cnt]=R[V[i]];
    else      L[cnt]=R[cnt]=cnt;
    V[i]=cnt;

    U[D[cnt]]=D[U[cnt]]=L[R[cnt]]=R[L[cnt]]=cnt;
}

inline void remove(const int &c)
{
    R[L[c]]=R[c];
    L[R[c]]=L[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]]--;
        }
}

inline void resume(const int &c)
{
    for (int i=U[c]; i!=c; i=U[i])
        for (int j=L[i]; j!=i; j=L[j])
        {
            D[U[j]]=j;
            U[D[j]]=j;
            S[C[j]]++;
        }
    L[R[c]]=R[L[c]]=c;
}

inline void build_graph()
{
    for (int i=0; i<=M; i++)
    {
        flag[i]=S[i]=0;
        U[i]=D[i]=i;
        R[i]=i+1;
        L[i+1]=i;
    }
    cnt=L[0]=M;
    R[M]=0;
    for (int i=0; i<=N; i++) V[i]=0;

    for (int i=0; i<K; i++)
        for (int j=0; j<K; j++) if (a[i][j]>=0)
            {
                flag[i*K+a[i][j]       +1     ]=1;
                flag[j*K+a[i][j]       +1+KK  ]=1;
                flag[pl[i][j]*K+a[i][j]+1+2*KK]=1;
                flag[i*K             +j+1+3*KK]=1;
            }

    int x,y1,y2,y3,y4;
    for (int i=0; i<K; i++)
        for (int j=0; j<K; j++) if (a[i][j]==-1)
            for (int k=0; k<K; k++)
            {
                x=i*KK+j*K+k;
                if (flag[y1=i*K+k+1]) continue;
                if (flag[y2=j*K+k+1+KK]) continue;
                if (flag[y3=pl[i][j]*K+k+1+2*KK]) continue;
                if (flag[y4=i*K+j+1+3*KK]) continue;
                add(x,y1);
                add(x,y2);
                add(x,y3);
                add(x,y4);
            }

    for (int i=1; i<=M; i++) if (flag[i]) remove(i);
}

bool dance()
{
    if (!R[0]) return true;

    int s(N),c;
    for (int i=R[0]; i; i=R[i]) if (S[i]<s) s=S[c=i];

    remove(c);
    for (int i=D[c]; i!=c; i=D[i])
    {
        a[H[i]/KK][H[i]/K%K]=H[i]%K;
        for (int j=R[i]; j!=i; j=R[j]) remove(C[j]);
        if (dance()) return true;
        for (int j=L[i]; j!=i; j=L[j]) resume(C[j]);
    }
    resume(c);

    return false;
}

inline int str2digit(char *s)
{
    if (s[0]=='?') return -1;
    else           return s[0]-'1';
}

int main()
{
    int i,j,blank=0;
    char s[4];
    while (scanf("%s",s)!=EOF)
    {
        if (blank) printf("\n");
        else       blank=1;
        a[0][0]=str2digit(s);
        for (j=1; j<9; j++)
        {
            scanf("%s",s);
            a[0][j]=str2digit(s);
        }
        for (i=1; i<9; i++)
          for (j=0; j<9; j++)
          {
              scanf("%s",s);
              a[i][j]=str2digit(s);
          }

        build_graph();
        dance();
        for (i=0; i<9; i++)
        {
            for (j=0; j<8; j++) printf("%d ",a[i][j]+1);
            printf("%d\n",a[i][8]+1);
        }
    }
    return 0;
}
找到影响效率的一个重要原因了!我把dance中的找c时的for (i=R[0]; i; i=R[i])改成for (i=L[0]; i; i=L[i])就让时间从2500ms+优化到290ms+(POJ3076)了,原因是每一格可填数的限制要强于每行每列每块的个数限制,而我的代码把每一格对应的列放在了后面,这一点还是去吃饭的路上想到的,看来及时吃饭很重要,在DLX算法中找列限制最强的找法是算法效率的一个关键点。

你可能感兴趣的:(HDOJ1426-DLX解sudoku)