题意
给出一张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;
}