【NOIP2009】【DLX】【位运算】T4 靶形数独 题解

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

这里写图片描述

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

由于求胜心切,小城找到了善于编程的你,让你帮他求出,对于给定的靶形数独,能够得到的最高分数。

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

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

样例输入 Sample Input
【输入输出样例 1】

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

【输入输出样例 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

样例输出 Sample Output
【输入输出样例 1】

2829

【输入输出样例 1】

2852

DLX 或者 位运算
1.DLX
每个数字当做一个精准覆盖问题,再处理九宫格
2.位运算
二进制处理矩阵

位运算:

#include <iostream>
#include <cstdlib>
#include <cstdio>

using namespace std;

const int mlen = 10;
int a[mlen][mlen],b[mlen][mlen];
int row[mlen],lie[mlen],line[mlen],ma[mlen];
int f[512],ans,node[mlen],cnt[mlen];

void init() {
    for(int i = 0; i < 9; i++) b[i][0] = b[0][i] = b[8][i] = b[i][8] = 6;
    for(int i = 1; i < 8; i++) b[i][1] = b[1][i] = b[7][i] = b[i][7] = 7;
    for(int i = 2; i < 7; i++) b[i][2] = b[2][i] = b[6][i] = b[i][6] = 8;
    for(int i = 3; i < 6; i++) b[i][3] = b[3][i] = b[5][i] = b[i][5] = 9;
    b[4][4] = 10; int t = 0;
    for(int i = 1, j = 0; i <= 511; i <<= 1, j++) f[i] = j;
    for(int i = 0; i < 9; i++)
        for(int j = 0; j < 9; j++) {
            scanf("%d",&a[i][j]);
            if(a[i][j] != 0){
                row[i] |= 1<<j;
                t = 1<<(a[i][j]-1);
                if((lie[i]&t) || (line[j]&t) || (ma[i/3*3+j/3]&t)) { printf("-1\n"); exit(0); }
                lie[i] |= t; line[j] |= t;
                ma[i/3*3+j/3] |= t;
            }else cnt[i]++;
        }
}

inline void sore() {
    int nowans = 0;
    for(int i = 0; i < 9; i++) for(int j = 0; j < 9; j++) nowans += a[i][j]*b[i][j];
    if(ans < nowans) ans = nowans;
}

void dfs(int t) {
    int pos,k;
    if(t == 9) { sore(); return; }
    int i = node[t];
    if(!cnt[i]) { dfs(t+1); return; }
    cnt[i]--;
    int p = (511^row[i])&(-(511^row[i]));
    row[i] |= p;
    int j = f[p];
    pos = 511^(lie[i]|line[j]|ma[i/3*3+j/3]);
    while(pos > 0) {
        k = pos&(-pos); pos ^= k;
        lie[i] |= k; line[j] |= k;
        ma[i/3*3+j/3] |= k; a[i][j] = f[k]+1;
        dfs(t);
        lie[i] ^= k; line[j] ^= k;
        ma[i/3*3+j/3] ^= k;
    }
    cnt[i]++; row[i] ^= p;
}

int main() {
    freopen("sudoku.in","r",stdin);
    freopen("sudoku.out","w",stdout);
    init();
    for(int i = 0; i < 9; i++) node[i] = i;
    for(int i = 0; i < 9; i++)
        for(int j = i+1; j < 9; j++)
            if(cnt[node[i]] > cnt[node[j]]) node[i] ^= node[j], node[j] ^= node[i], node[i] ^= node[j];
    int tot = 0;
    while(!cnt[node[tot]]) tot++;
    dfs(tot);
    if(!ans) { printf("-1\n"); return 0; }
    printf("%d\n",ans);
    return 0;
}

DLX(By Anantheparty):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define INF 2100000000
#define ll long long
#define clr(x)  memset(x,0,sizeof(x));
#define M 325

using namespace std;

struct link2
{
    link2 *up,*down,*right,*left,*col;
    int row,count,lie;
}*head,*c[M],*lik[5*M],dizhi[5*M];

int T,a[90],ans[M],sta,n,Ans;

int get_num(int a)
{
    int x=((a-1)/9)+1,y=((a-1)%9)+1;
    int m1=min(x-1,9-x),m2=min(y-1,9-y),mi=min(m1,m2);
    return mi+6;
}

void remove(link2 *C)
{
    C->left->right=C->right;
    C->right->left=C->left;

    link2 *i=C->down;
    while(i!=C)
    {
        link2 *j=i->right;
        while(j!=i)
        {
            j->up->down=j->down;
            j->down->up=j->up;
            j=j->right;
            j->col->count--;
        }
        i=i->down;
    }
}

void resume(link2 *C)
{
    C->left->right=C;
    C->right->left=C;

    link2 *i=C->down;
    while(i!=C)
    {
        link2 *j=i->right;
        while(j!=i)
        {
            j->up->down=j;
            j->down->up=j;
            j=j->right;
            j->col->count++;
        }
        i=i->down;
    }
}

int t;

void addline(int *index,int row)
{
    for(int i=1;i<=4;i++)
    {
        t++;
        lik[t]=&dizhi[t+T];
        if(i==1)
        {
            lik[t]->left=lik[t];
            lik[t]->right=lik[t];
        }
        else
        {
            lik[t]->left=lik[t-1];
            lik[t]->right=lik[t-1]->right;
            lik[t-1]->right->left=lik[t];
            lik[t-1]->right=lik[t];
        }
        int j=index[i];
        c[j]->count++;
        lik[t]->up=c[j]->up;
        c[j]->up->down=lik[t];
        lik[t]->down=c[j];
        c[j]->up=lik[t];
        lik[t]->col=c[j];
        lik[t]->row=row;
        lik[t]->lie=j;
    }
}

void bulid()
{
    int m=324;
    head=&dizhi[T++];
    for(int i=1;i<=m;i++)c[i]=&dizhi[T++];
    head->right=c[1];
    head->left=c[m];
    for(int i=1;i<=m;i++)
    {
        c[i]->left=i==1?head:c[i-1];
        c[i]->right=i==m?head:c[i+1];
        c[i]->up=c[i]->down=c[i];
        c[i]->count=0;
        c[i]->lie=i;
    }
    for(int i=1;i<=81;i++)
    {
        if(!a[i])continue;
        int y=(i-1)/9+1,x=(i-1)%9+1;
        int p1=i,p2=81+(y-1)*9+a[i];
        int p3=162+(x-1)*9+a[i],p4=243+((y-1)/3*3+(x-1)/3)*9+a[i];
        c[p1]->count=c[p2]->count=c[p3]->count=c[p4]->count=-1;
        c[p1]->left->right=c[p1]->right;c[p1]->right->left=c[p1]->left;
        c[p2]->left->right=c[p2]->right;c[p2]->right->left=c[p2]->left;
        c[p3]->left->right=c[p3]->right;c[p3]->right->left=c[p3]->left;
        c[p4]->left->right=c[p4]->right;c[p4]->right->left=c[p4]->left;
    }

    for(int i=1;i<=9;i++)
        for(int j=1;j<=9;j++)
            for(int k=1;k<=9;k++)
            {
                if(a[(i-1)*9+j])continue;
                int p1=(i-1)*9+j;
                int p2=81+(i-1)*9+k;
                int p3=162+(j-1)*9+k;
                int p4=243+((i-1)/3*3+(j-1)/3)*9+k;
                if(c[p1]->count==-1||c[p2]->count==-1||c[p3]->count==-1||c[p4]->count==-1)continue;
                int index[5]={0,p1,p2,p3,p4};
                addline(index,((i-1)*9+j)*10+k);
            }
}

bool dance(int x)
{
    if(head->right==head)
    {
        for(int i=1;i<=sta;i++)
            a[ans[i]/10]=ans[i]%10;
        //printf("%d:\n",n+1);
        int temp=0;
        for(int i=1;i<=81;i++)
            temp+=get_num(i)*a[i];
        if(temp>Ans)
            Ans=temp;
        return 0;   //就是这里
    }
    link2 *i=head->right->right;
    link2 *temp=head->right;
    while(i!=head)
    {
        if(i->count<temp->count)
            temp=i;
        i=i->right;
    }
    if(temp->down==temp)return 0;
    remove(temp);
    i=temp->down;
    while(i!=temp)
    {
        link2 *j=i->right;
        ans[++sta]=i->row;
        while(j!=i)
        {
            remove(j->col);
            j=j->right;
        }
        if(dance(x+1))return 1;
        j=i->left;
        while(j!=i)
        {
            resume(j->col);
            j=j->left;
        }
        ans[sta]=0;
        sta--;
        i=i->down;
    }
    resume(temp);
    return 0;
}

int main()
{
    for(int i=1;i<=81;i++)
        scanf("%d",&a[i]);
    t=T=0;
    bulid();
    dance(0);
    if(Ans==0)cout<<-1;
    else cout<<Ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值