核心是用位运算简化操作,并根据每行的“0”的数量确定枚举的顺序
详见代码及代码注释
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
int target[10][10]={
{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 a[10][10];
int lg2[1<<10], line[10];
//lg2[i]:快速算对数(用于找到拿出来的那个数字是几)
//line[i]:第i行有那些位置填上了
int row[10], col[10], grid[10];
//分别记录行/列/块中的已选元素
int h[10], st[10];
//st数组:搜索顺序
bool flag = 0;
int Maxsum = 0;
const int allset = (1 << 9) - 1;
void init() {
for(int i = 0; i < 10; i++)
lg2[1<<i] = i;
}
int lowbit(int x) {
return x&(-x);
}
void add(int x, int y, int pos, int p) {
row[x] |= p, col[y] |= p, grid[pos] |= p;
}
void del(int x, int y, int pos, int p) {
row[x] -= p, col[y] -= p, grid[pos] -= p;
}
bool cmp(int x, int y) {
return h[x] < h[y];
}
void dfs(int c, int sum) {
if(c == 9) {
if(Maxsum < sum)
Maxsum = sum;
flag = true; return;
}
int x = st[c], setx = allset - line[x];
//setx本行还能填什么数字
if(!setx) {
dfs(c+1, sum);
return;
} //本行已经搜索结束;
int sety = lowbit(setx), y = lg2[sety];
//sety : 待处理的最后一位数
//y:y的具体位置;
line[x] |= sety;
int pos = x/3*3 + y/3; //所属于哪个块
int num = allset - (row[x]|col[y]|grid[pos]);
//num:还能填哪些数
while(num) {
int p = lowbit(num);
num -= p;
//每次去掉搜索过的数;
a[x][y] = lg2[p] + 1;
add(x, y, pos, p);
if(setx == sety) //如果本行只能填一个数字
dfs(c+1, sum+a[x][y]*target[x][y]);
else
dfs(c, sum+a[x][y]*target[x][y]);
a[x][y] = 0;
del(x, y, pos, p);
}
line[x] -= sety;
} //每次递归搜索的是一个点,而不是一行;
int main() {
init();
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++) {
scanf("%d", &a[i][j]);
if(a[i][j]) {
int num = a[i][j];
int pos = i/3*3 + j/3;
add(i, j, pos, 1<<num-1);
line[i] |= 1 << j;
Maxsum += num*target[i][j];
} else h[i]++;
}
for(int i = 0; i < 9; i++)
st[i] = i;
sort(st, st+9, cmp);
dfs(0, Maxsum);
if(flag)
printf("%d\n", Maxsum);
else
puts("-1");
return 0;
}