6670903752021072936960

据说在2003年这个数字被计算出来了。

我们也可以计算一下呦!

方法呢?当然是记忆化搜索。

为了简化状态,我们每个3*3一起填。

记录每个列、每个行当前剩下的数字的集合。开一个数组来存储搜索到当前状态的方法数。

当然,状态太多了,数组是开不下的。于是,我们要充分利用对称性来减少状态数量。

第一部分的对称性来自数字的对称。1~9是可以相互替换的。这样,我们可以把状态的个数减少很多。

第二部分的对称来自行和列的对称。注意,我们把第1、2、3行相互交换是无所谓的,第1、2、3列之间也是可以整列交换的。于是,状态的个数还可以减少6的4次方。

最后的对称来自整3行的对称。在填完第3~6行第1~3列之后,我们可以把4~6列和7~9列整体交换。这样,状态个数再次除以2。

状态总数缩小到了几百万。可以存下了。

 

写代码出来是一个不容易的工作。

经过很努力的优化,我的程序可以在4分钟内出结果了。

#include <cstdio>
#include <algorithm>
#include <string.h>
#include <ctime>
using namespace std;
const int Max3=280;
const int NMax=Max3*Max3*Max3;
const long long Mod=0;
long long dp1[NMax],dp2[NMax];
int repr1[NMax][4],repr2[NMax][4];
int A[12];
long long cur;
int bitcount[512];
int comb[280];
const int AMax=50;
int Ans[50];
int idcnt=0;
int getid(const int *a,int y){
    idcnt++;
    int perm[9],n=0;
    for (int i=0;i<9;i++)perm[i]=-1;
    int b[12];
    for (int i=0;i<12;i++){
        if (bitcount[a[i]]<=3)b[i]=a[i];
        else b[i]=a[i]^511;
    }
    int val[9];
    for (int i=0;i<9;i++)val[i]=i;
    for (int i=0;i<12;i++){
        int s=b[i];
        if (s){
            int j=__builtin_ctz(s);
            val[j]|=((1<<20)>>i);
            s&=s-1;
            j=__builtin_ctz(s);
            val[j]|=((1<<20)>>i);
            s&=s-1;
            j=__builtin_ctz(s);
            val[j]|=((1<<20)>>i);
            //for (int j=0;j<9;j++)if (s&(1<<j))val[j]|=(1<<(20-i));
        }
    }
    if (b[0]){
        for (int i=0;i<3;i++){
            int s=b[i];
            int ps[3];
            ps[0]=__builtin_ctz(s);
            s&=s-1;
            ps[1]=__builtin_ctz(s);
            s&=s-1;
            ps[2]=__builtin_ctz(s);
            //for (int j=0;j<9;j++)
                //if (s&(1<<j))ps[pc++]=j;
            if (val[ps[0]]<val[ps[1]])swap(ps[0],ps[1]);
            if (val[ps[0]]<val[ps[2]])swap(ps[0],ps[2]);
            if (val[ps[1]]<val[ps[2]])swap(ps[1],ps[2]);
            perm[ps[0]]=n;
            perm[ps[1]]=n+1;
            perm[ps[2]]=n+2;
            n+=3;
        }
        for (int i=3;i<12;i++){
            int t=0,s=b[i];
            if (s){
                int j=__builtin_ctz(s);
                t|=(1<<perm[j]);
                s&=s-1;
                j=__builtin_ctz(s);
                t|=(1<<perm[j]);
                s&=s-1;
                j=__builtin_ctz(s);
                t|=(1<<perm[j]);
            }
            //for (int j=0;j<9;j++)if (s&(1<<j)){
                //if (perm[j]==-1){
                    //perm[j]=n++;
                //}
                //t|=(1<<perm[j]);
            //}
            b[i]=t;
        }
    }else{
        for (int i=0;i<12;i++){
            int t=0,s=b[i];
            if (s){
                int j=__builtin_ctz(s);
                if (perm[j]==-1)perm[j]=n++;
                t|=(1<<perm[j]);
                s&=s-1;
                j=__builtin_ctz(s);
                if (perm[j]==-1)perm[j]=n++;
                t|=(1<<perm[j]);
                s&=s-1;
                j=__builtin_ctz(s);
                if (perm[j]==-1)perm[j]=n++;
                t|=(1<<perm[j]);
            }
            //for (int j=0;j<9;j++)if (s&(1<<j)){
                //if (perm[j]==-1){
                    //perm[j]=n++;
                //}
                //t|=(1<<perm[j]);
            //}
            b[i]=t;
        }
    }
    int r=0;
    for (int i=3;i<12;i+=3){
        if (b[i]>b[i+1])swap(b[i],b[i+1]);
        if (b[i]>b[i+2])swap(b[i],b[i+2]);
        if (b[i+1]>b[i+2])swap(b[i+1],b[i+2]);
    }
    if (y==0){
        if (b[6]>b[9]){
            swap(b[6],b[9]);
            swap(b[7],b[10]);
            swap(b[8],b[11]);
        }
    }
    for (int i=3;i<12;i+=3){
        int u=b[i],v=b[i+1],w=b[i+2];
        if (u==0 && v==0 && w==0)r=r*Max3;
        else{
            int t=(u<<18)|(v<<9)|w;
            int p=0,q=Max3;
            while (q-p>1){
                int m=(q+p)>>1;
                if (comb[m]<=t)p=m;
                else q=m;
            }
            r=r*Max3+p;
        }
    }
    for (int i=0;i<4;i++){
        int t=0;
        for (int j=0;j<3;j++)
            t|=a[i*3+j]<<(j*9);
        repr1[r][i]=t;
    }
    return r;
}
void dfs(int j,int x,int y,int u){
    if (y==3)x++,y=0;
    if (x==3){
        int B[12];
        for (int i=0;i<j*3;i++)B[i]=A[i];
        for (int i=j*3;i<j*3+3;i++)B[i]=A[i+3];
        for (int i=j*3+3;i<j*3+6;i++)B[i]=A[i-3];
        for (int i=j*3+6;i<12;i++)B[i]=A[i];
        int k=getid(B,j);
        dp1[k]+=cur;
        if (Mod && dp1[k]>=Mod)
            dp1[k]-=Mod;
    }else{
        for (int i=0;i<9;i++)if (!((1<<i)&u) && !((1<<i)&(A[j*3+x]|A[j*3+3+y]))){
            A[j*3+x]|=(1<<i);
            A[j*3+3+y]|=(1<<i);
            dfs(j,x,y+1,u|(1<<i));
            A[j*3+x]^=(1<<i);
            A[j*3+3+y]^=(1<<i);
        }
    }
}
int ways[3],n1;
void dfs1(int a,int u){
    if (a==3){
        if (ways[0]<ways[1] && ways[1]<ways[2]){
            comb[n1]=(ways[0]<<18)|(ways[1]<<9)|ways[2];
            n1++;
        }
        return;
    }
    for (int i=0;i<9;i++)if (!((1<<i)&u)){
        for (int j=0;j<i;j++)if (!((1<<j)&u)){
            for (int k=0;k<j;k++)if (!((1<<k)&u)){
                ways[a]=(1<<i)|(1<<j)|(1<<k);
                dfs1(a+1,u|ways[a]);
            }
        }
    }
}
void showret(){
    int l=AMax-1;
    while (l && Ans[l]==0)l--;
    for (int i=l;i>=0;i--)printf("%d",Ans[i]);
    puts("");
}
int main()
{
    bitcount[0]=0;
    for (int i=1;i<512;i++)bitcount[i]=bitcount[i>>1]+(i&1);
    n1=0;
    dfs1(0,0);
    sort(comb,comb+n1);
    memset(dp1,0,sizeof(dp1));
    for (int i=0;i<12;i++)A[i]=0;
    dp1[getid(A,-1)]=1;
    memset(Ans,0,sizeof(Ans));
    for (int i=0;i<3;i++){
        for (int j=0;j<3;j++){
            printf("i=%d j=%d\n",i,j);
            memcpy(dp2,dp1,sizeof(dp2));
            memcpy(repr2,repr1,sizeof(repr2));
            memset(dp1,0,sizeof(dp1));
            int cnt=0;
            for (int k=0;k<NMax;k++)if (dp2[k]){
                cur=dp2[k];
                if (i==0 && j==1)cur/=362880;
                for (int l=0;l<12;l++)
                    A[l]=(repr2[k][l/3]>>(9*(l%3)))&511;
                if (j==0){
                    for (int l=11;l>=3;l--)A[l]=A[l-3];
                    A[0]=A[1]=A[2]=0;
                }
                if (k%1000==0){
                    printf("on %d got ",k);
                    for (int i=0;i<12;i++){
                        printf("(");
                        for (int j=0;j<9;j++)
                            if (A[i]&(1<<j))printf("%d",j);
                        printf(")");
                    }
                    printf(" with %lld\n",cur);
                    
                }
                dfs(j,0,0,0);
                cnt++;
            }
            printf("cnt=%d\n",cnt);
        }
    }
    for (int i=0;i<362880;i++){
        long long t=dp1[0];
        for (int j=0;j<AMax;j++){
            t+=Ans[j];
            Ans[j]=t%10;
            t/=10;
        }
    }
    showret();
    return 0;
}