【noip2009】靶形数独(搜索+二进制优化)

题意

    给出一张9*9的未填满的数独,以及每个网格的权值val。求Max{∑val[i][j]*a[i][j]},a[i][j]表示一张合法数独第i行j列上所填数字。

数据范围

    数独中非0数的个数不少于24

思路

    搜索+二进制优化
    按照一般填数独的策略,每次选择能够填的数最少的格子填。使用二进制表示行H,列L,块X上能够填的数,第i行j列处于k块的格子能填的数的二进制表示即为H[i]&L[j]&X[Loc[i][j]]。
    预处理出十进制2^p的p用于枚举可填的颜色。预处理1<<9种情况分别能够填的数的数量cnt,cnt[i] = cnt[i - lowbit(i)] + 1。
    lowbit(i)取i的二进制表示最右端的1所表示的十进制数。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define lowbit(x) (x&(-x))

const int Loc[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}}; 
const int Val[9][9] = {{6,6,6,6,6,6,6,6,6},
                       {6,7,7,7,7,7,7,7,6},
                       {6,7,8,8,8,8,8,7,6},
                       {6,7,8,9,9,9,8,7,6},
                       {6,7,8,9,10,9,8,7,6},
                       {6,7,8,9,9,9,8,7,6},
                       {6,7,8,8,8,8,8,7,6},
                       {6,7,7,7,7,7,7,7,6},
                       {6,6,6,6,6,6,6,6,6}};
int ans = -1;

int yes[10];
int H[10],L[10],X[10];
int rep[1<<9],cnt[1<<9];

int Init() {
    for(int i = 0; i < 9; ++i) {
        rep[1<<i] = i;
        H[i] = L[i] = X[i] = (1<<9)-1;
    }
    for(int i = 1; i < (1<<9); ++i)
        cnt[i] = cnt[i-lowbit(i)] + 1;

    int val, ret = 0, t;
    for(int i = 0; i < 9; ++i)
        for(int j = 0; j < 9; ++j) {
            scanf("%d",&val);
            if(val) {
                t = 1 << val-1;
                H[i] ^= t;
                L[j] ^= t;
                X[Loc[i][j]] ^= t; 
                ret += val * Val[i][j];
            }
            else yes[i] |= (1<<j);
        }
    return ret;
}

void Dfs(int sum) {
    int t,j,tot,val,x,y,mn = 10;
    for(int i = 0; i < 9; ++i) {
        t = yes[i];
        while(t) {
            j = lowbit(t), t ^= j, j = rep[j];
            tot = cnt[H[i]&L[j]&X[Loc[i][j]]];
            if(tot < mn) {
                mn = tot; 
                x = i, y = j;
            }
        }
    }

    if(mn == 10) {
        if(sum > ans) ans = sum;
        return;
    }

    yes[x] ^= 1<<y;
    t = H[x]&L[y]&X[Loc[x][y]];
    while(t) {
        val = lowbit(t), t ^= val;
        H[x] ^= val, L[y] ^= val, X[Loc[x][y]] ^= val;
        Dfs(sum + Val[x][y]*(rep[val]+1));
        H[x] ^= val, L[y] ^= val, X[Loc[x][y]] ^= val;
    }
    yes[x] ^= 1<<y;
}

int main() {
    Dfs(Init());
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值