nankai 2082: 靶形数独 数独(9*9)求所有解 DLX+精确覆盖

 
2082: 靶形数独

Time Limit: 2000 ms    Memory Limit: 131072 kB  
Judge type: Multi-cases (Detailed Mode - 20 cases)
Total Submit : 181  (41 users)    Accepted Submit : 25  (15 users)    Page View : 1792 
Font Style: Aa Aa Aa
    小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z博士请教,Z 博士拿出了他最近发明的“靶形数独” ,作为这两个孩子比试的题目。
     靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格高的小九宫格(用粗黑色线隔开的) 。在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1到 9 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高。 (如图)

    上图具体的分值分布是:最里面一格(黄色区域)为 10 分,黄色区域外面的一圈(红色区域)每个格子为 9 分,再外面一圈(蓝色区域)每个格子为 8分,蓝色区域外面一圈(棕色区域)每个格子为 7分,最外面一圈(白色区域)每个格子为 6 分,如上图所示。
     比赛的要求是:每个人必须完成一个给定的数独(每个给定数独可能有不同的填法) ,而且要争取更高的总分数。而这个总分数即 每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 2829。游戏规定,将以总分数的高低决出胜负。
 
     由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。

Input

一共 9 行。每行 9 个整数(每个数都在 0—9 的范围内) ,表示一个尚未填满的数独方格,未填的空格用“0”表示。每两个数字之间用一个空格隔开。

Output

输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数-1。

Sample Input

7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2

Sample Output

2829

Hint

40%的数据,数独中非 0数的个数不少于 30。
80%的数据,数独中非 0数的个数不少于 26。
100%的数据,数独中非 0 数的个数不少于 24。

Source

NOIP 2009 提高组

Best User : nehzilrz


 

 

#include
#include
#include
#include
#include
using namespace std;
#define N 1005
#define V 1020005
int U[V],D[V];
int L[V],R[V];
int C[V];
int H[N],S[N];
int mark[V];
int size,n,m,OK[N],flag;
int tnum[V];
void Link(int r,int c)
{
    S[c]++;C[size]=c;
    U[size]=U[c];D[U[c]]=size;
    D[size]=c;U[c]=size;
    if(H[r]==-1) H[r]=L[size]=R[size]=size;
    else
    {
        L[size]=L[H[r]];R[L[H[r]]]=size;
        R[size]=H[r];L[H[r]]=size;
    }
    mark[size]=r;
    size++;
}
void remove(int c)//删除列
{
    int i,j;
    L[R[c]]=L[c];
    R[L[c]]=R[c];
    for(i=D[c];i!=c;i=D[i])
    {
        for(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)
{
    int i,j;
    for(i=U[c];i!=c;i=U[i])
    {
        for(j=L[i];j!=i;j=L[j])
        {
            U[D[j]]=j;D[U[j]]=j;
            S[C[j]]++;
        }
    }
    L[R[c]]=c;
    R[L[c]]=c;
}
int ans;
int fen[100],x[N],y[N];//输出所有解用的
void Dance(int k)
{
    int i,j,Min,c;
    if(!R[0])
    {
        flag=1;//标记有解
        //sort(OK, OK+k);
        int cnt=0;
        for(int i=0;i         {
            int t=mark[OK[i]];
            int pos=x[t]*9+y[t];
            cnt+=fen[pos]*tnum[t];
        }
        ans=max(ans,cnt);
        return;
    }
    for(Min=N,i=R[0];i;i=R[i])
        if(S[i]     remove(c);//删除该列
    for(i=D[c];i!=c;i=D[i])
    {
        OK[k]=i;
        for(j=R[i];j!=i;j=R[j])
            remove(C[j]);
        Dance(k+1);
        //if(flag) return;//只要一组解
        for(j=L[i];j!=i;j=L[j])
            resume(C[j]);
    }
    resume(c);
}
/*
试构造一个矩阵,其中以行表示概然,以列表示约束。
行所表示的概然状态为(r,c,k)即在棋盘r行c列放置数字k。
列所表示的约束分做四种,即改当前方案r行中是否放置数k,c列中是否放置数k,
(r,c)格中是否放置数k以及块b(即所属区域)是否放置数k。
因此行总共有N*N*N=9*9*9=729个,列总共有9*9*4=324个,要求取若干数字摆放的方案(行),
使每个数字在棋盘的行、列、区域块中(列)只出现一次(1个‘1’),问题转化为729*324的
矩阵的精确覆盖。特别的,(r,c)格的约束保证了我们最后可行解的一定为N*N。
*/
char str[100];
int mat[N][N];
int a[10][10];
int tof(int x,int y)
{
    return x*9+y;
}
int main()
{
    for(int i=0;i<5;i++)
    {
        for(int j=i;j<9-i;j++)
            fen[tof(i,j)]=i+6,fen[tof(9-i-1,j)]=i+6,
            fen[tof(j,i)]=i+6,fen[tof(j,9-i-1)]=i+6;
    }
    //for(int i=0;i<9;i++){for(int j=0;j<9;j++) printf("%-3d",fen[tof(i,j)]);printf("\n");}
    while(scanf("%d",&a[0][0])==1)
    {
        for(int i=0;i<9;i++) for(int j=0;j<9;j++)
            if(i||j) scanf("%d",&a[i][j]);
        //DLX
        m=9*9*4;//列数
        n=0;//构图花费时间比较多  应该直接构图 不用mat数组  这样会快很多
        for(int i=0;i<=m;i++)
        {
            S[i]=0;
            D[i]=U[i]=i;
            L[i+1]=i;R[i]=i+1;
        }R[m]=0;
        size=m+1;
        memset(H,-1,sizeof(H));
        memset(mark,0,sizeof(mark));//不用mat数组,这样可以加快速度
        for(int i=1;i<=9;i++)
        {
            for(int j=1;j<=9;j++)
            {
                int row=i;
                int col=j;
                int kuai=(i-1)/3*3+(j-1)/3+1;
                int pos=(i-1)*9+j;
                if(a[i-1][j-1]==0)//可以放数字
                {
                    for(int k=1;k<=9;k++)
                    {
                        ++n;
                        tnum[n]=k;x[n]=i-1,y[n]=j-1;
                        Link(n,(row-1)*9+k);//行
                        Link(n,81+(col-1)*9+k);//列
                        Link(n,162+(kuai-1)*9+k);//块
                        Link(n,243+pos);//位置
                    }
                }
                else//已经放数字
                {
                    int k=a[i-1][j-1];
                    ++n;
                    tnum[n]=k;x[n]=i-1,y[n]=j-1;
                    Link(n,(row-1)*9+k);//行
                    Link(n,81+(col-1)*9+k);//列
                    Link(n,162+(kuai-1)*9+k);//块
                    Link(n,243+pos);//位置
                }
            }
        }
        flag=0,ans=0;
        Dance(0);
        if(flag) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}
/*
7 0 0 9 0 0 0 0 1
1 0 0 0 0 5 9 0 0
0 0 0 2 0 0 0 8 0
0 0 5 0 2 0 0 0 3
0 0 0 0 0 0 6 4 8
4 1 3 0 0 0 0 0 0
0 0 7 0 0 2 0 9 0
2 0 1 0 6 0 8 0 4
0 8 0 5 0 4 0 1 2


0 0 0 7 0 2 4 5 3
9 0 0 0 0 8 0 0 0
7 4 0 0 0 5 0 1 0
1 9 5 0 8 0 0 0 0
0 7 0 0 0 0 0 2 5
0 3 0 5 7 9 1 0 8
0 0 0 6 0 1 0 0 0
0 6 0 9 0 0 0 0 1
0 0 0 0 0 0 0 0 6

2829   2852
*/

 

你可能感兴趣的:(acm_数据结构)