题意:求一个像靶子一样加权后的数独的最大权值,无解输出-1。权值等于每个格子上填的数与这一圈的权值之积的和。
写代码之前想了很久……剪枝?估价?
最后抱着试试看的心态,只使用了曾看见CS用过的那种优化:先填限制最多的格子。结果这样就足够切掉此题了。
我的代码可以简化。前面打了两个表,看过别人的代码后才想到可以精简。比如,对于会做整数除法的人,sq[3][3]
就够了。
先前每次dfs都算一遍r
、c
、s
。意识到做了许多无用功,改进了一下,总时间快了1s多……
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int w[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}},
sq[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}},
inf = 1<<30;
int M[9][9], r[9], c[9], s[9], ans;
inline int count(int m)
{
int cnt = 0;
for (int i = 1; i <= 9; ++i)
cnt += !(m & (1<<i));
return cnt;
}
void solve(int n)
{
if (n > 81) {
int tot = 0;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j)
tot += w[i][j] * M[i][j];
ans = max(ans, tot);
return;
}
int mn = inf, x, y, num;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j) {
int m = r[i] | c[j] | s[sq[i][j]], cnt = count(m);
if (!M[i][j] && cnt < mn) {
mn = cnt;
num = m;
x = i;
y = j;
}
}
if (mn == inf)
return;
for (int i = 9, m = 1<<9; i >= 1; --i, m >>= 1) {
if (~num & m) {
M[x][y] = i;
r[x] |= m;
c[y] |= m;
s[sq[x][y]] |= m;
solve(n+1);
r[x] &= ~m;
c[y] &= ~m;
s[sq[x][y]] &= ~m;
}
}
M[x][y] = 0;
}
int main()
{
int n = 0;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j) {
scanf("%d", &M[i][j]);
if (M[i][j]) {
++n;
int m = 1<<M[i][j];
r[i] |= m;
c[j] |= m;
s[sq[i][j]] |= m;
}
}
solve(n+1);
printf("%d\n", ans ? ans : -1);
return 0;
}