题解 - [Luogu P1074] 靶形数独

题目链接

原始题面

1000 ms / 125.00 MB

题目描述

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的 “靶形数独”, 作为这两个孩子比试的题目

靶形数独的方格同普通数独一样,在 \(9\) 格宽且 \(9\) 格高的大九宫格中有 \(9\)\(3\) 格宽且 \(3\) 格高的小九宫格(用粗黑色线隔开的). 在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 \(1\)\(9\) 的数字。每个数字在每个小九宫格内不能重复出现,每个数字在每行、每列也不能重复出现。但靶形数独有一点和普通数独不同,即每一个方格都有一个分值,而且如同一个靶子一样,离中心越近则分值越高. (如图)

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

总分数即每个方格上的分值和完成这个数独时填在相应格上的数字的乘积的总和。如图,在以下的这个已经填完数字的靶形数独游戏中,总分数为 \(2829\). 游戏规定,将以总分数的高低决出胜负

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

输入输出格式

输入格式

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

输出格式

输出共 \(1\) 行。输出可以得到的靶形数独的最高分数。如果这个数独无解,则输出整数 \(-1\)

输入输出样例

输入样例 #1

1
2
3
4
5
6
7
8
9
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

输出样例 #1

1
2829

输入样例 #2

1
2
3
4
5
6
7
8
9
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

输出样例 #2

1
2852

说明

数据规模与约定

  • 对于 \(40\%\) 的数据,数独中非 \(0\) 数的个数不少于 \(30\)
  • 对于 \(80\%\) 的数据,数独中非 \(0\) 数的个数不少于 \(26\)
  • 对于 \(100\%\) 的数据,数独中非 \(0\) 数的个数不少于 \(24\)

解题思路

暴搜或 DLX, 这里讲 DLX 做法

状态这样选取:

  • 单次操作为在某行某列放某数字,所以总的操作种数一共 \(9^3=729\)
  • \(4\times 9^2=324\) 维向量表示单次操作后的影响,其中
    • \(1\sim 81\) 维表示在某位置放了数 (某位置只能放一个数)
    • \(82\sim 162\) 维表示某行放了某个数 (某行只能放一个数)
    • \(163\sim 243\) 维表示某列放了某个数 (某列只能放一个数)
    • \(244\sim 324\) 维表示某宫放了某个数 (某宫只能放一个数)

代码参考

Show code

Luogu_P1074view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
* @Author: Tifa
* @Description: From <https://github.com/Tiphereth-A/CP-archives>
* !!! ATTENEION: All the context below is licensed under a
* GNU Affero General Public License, Version 3.
* See <https://www.gnu.org/licenses/agpl-3.0.txt>.
*/
#include <bits/stdc++.h>
using namespace std;
class DLX {
protected:
struct Node {
size_t l, r, u, d, row, col;
constexpr Node(size_t l = 0,
size_t r = 0,
size_t u = 0,
size_t d = 0,
size_t row = 0,
size_t col = 0)
: l(l), r(r), u(u), d(d), row(row), col(col) {}
friend std::ostream &operator<<(std::ostream &os, Node const &data) {
return os << data.l << ' ' << data.r << ' ' << data.u << ' ' << data.d
<< ' ' << data.row << ' ' << data.col;
}
};
std::vector<Node> data;
std::vector<size_t> cnt_col;
bool multians;
#define l_(x) data[x].l
#define r_(x) data[x].r
#define u_(x) data[x].u
#define d_(x) data[x].d
#define row_(x) data[x].row
#define col_(x) data[x].col
#define for_(i, start, dir) \
for (size_t start__ = (start), i = dir##_(start__); i != start__; \
i = dir##_(i))
void remove_(size_t col) {
r_(l_(col)) = r_(col);
l_(r_(col)) = l_(col);
for_(i, col, d)
for_(j, i, r) {
u_(d_(j)) = u_(j);
d_(u_(j)) = d_(j);
--cnt_col[col_(j)];
}
}
void resume_(size_t col) {
r_(l_(col)) = l_(r_(col)) = col;
for_(i, col, u)
for_(j, i, r) {
u_(d_(j)) = d_(u_(j)) = j;
++cnt_col[col_(j)];
}
}
bool dance_(std::vector<size_t> &ans_,
std::function<void(std::vector<size_t> const &)> const &func_) {
size_t now = r_(0);
if (now == 0) return func_(ans_), true;
for_(i, 0, r)
if (cnt_col[i] < cnt_col[now]) now = i;
remove_(now);
bool ret = false;
for_(i, now, d) {
ans_.push_back(row_(i));
for_(j, i, r) remove_(col_(j));
ret |= dance_(ans_, func_);
for_(j, i, l) resume_(col_(j));
if (!multians && ret) return true;
ans_.pop_back();
}
resume_(now);
return ret;
}
void ins_row_(size_t row, const std::vector<size_t> &cols) {
assert(row > 0);
size_t sz = data.size();
for (size_t i = 0; i < cols.size(); ++i) {
data.emplace_back(
sz + i - 1, sz + i + 1, u_(cols[i]), cols[i], row, cols[i]);
u_(cols[i]) = d_(u_(cols[i])) = sz + i;
++cnt_col[cols[i]];
if (d_(cols[i]) == cols[i]) d_(cols[i]) = sz + i;
}
r_(l_(sz) = data.size() - 1) = sz;
}

public:
explicit DLX(size_t width, bool multi_ans = false)
: data(), cnt_col(), multians(multi_ans) {
reset(width);
}
explicit DLX(std::vector<std::vector<bool>> const &grid,
bool multi_ans = false)
: data(), cnt_col(), multians(multi_ans) {
reset_grid(grid);
}
friend std::ostream &operator<<(std::ostream &os, DLX const &dlx) {
os << dlx.data[0].l << ' ' << dlx.cnt_col.size() << ' ' << dlx.data.size()
<< '\n';
for (size_t i = 1; i < dlx.cnt_col.size(); ++i)
os << dlx.cnt_col[i] << " \n"[i == dlx.cnt_col.size() - 1];
for (size_t i = 0; i < dlx.data.size(); ++i)
os << i << ": " << dlx.data[i] << '\n';
return os;
}
void reset(size_t width) {
assert(width > 0);
data.clear();
cnt_col.resize(width + 1, 0);
for (size_t i = 0; i <= width; ++i)
data.emplace_back(i - 1, i + 1, i, i, 0, i);
r_(l_(0) = width) = 0;
}
void reset_grid(std::vector<std::vector<bool>> const &grid) {
size_t col = grid[0].size();
for (size_t i = 1; i < grid.size(); ++i)
col = std::max(col, grid[i].size());
reset(col);
std::vector<size_t> _;
for (size_t i = 0; i < grid.size(); ++i) {
_.clear();
for (size_t j = 0; j < grid[i].size(); ++j)
if (grid[i][j]) _.push_back(j + 1);
if (!_.empty()) ins_row_(i + 1, _);
}
}
std::pair<bool, std::vector<size_t>>
dance(std::function<void(std::vector<size_t> const &)> func =
[](auto const &) {}) {
std::vector<size_t> ans;
return {dance_(ans, func), ans};
}
#undef l_
#undef r_
#undef u_
#undef d_
#undef row_
#undef col_
#undef for_
};
int sdk[9][9];
template <class Ch,
class Tr,
class Ct,
std::enable_if_t<std::is_same_v<decltype(std::declval<Ct>().begin()),
typename Ct::iterator> &&
std::is_same_v<decltype(std::declval<Ct>().end()),
typename Ct::iterator>> * = nullptr>
std::basic_ostream<Ch, Tr> &operator<<(std::basic_ostream<Ch, Tr> &os,
const Ct &x) {
if (x.begin() == x.end()) return os;
for (auto it = x.begin(); it != x.end() - 1; ++it) os << *it << ' ';
os << x.back();
return os;
}
const int score[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 main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j) cin >> sdk[i][j];
vector<vector<bool>> maps(729, vector<bool>(324));
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j) {
for (int k = 1; k <= 9; ++k) {
if (sdk[i][j] && sdk[i][j] != k) continue;
auto _ = (k - 1) * 81 + i * 9 + j;
maps[_][i * 9 + j] = maps[_][i * 9 + k + 80] =
maps[_][j * 9 + k + 161] =
maps[_][(i / 3 * 3 + j / 3) * 9 + k + 242] = true;
}
}
int max_score = -1;
auto func = [&](const vector<size_t> &res) {
for (auto &&_ : res) {
auto k = (_ - 1) / 81 + 1, i = (_ - 1) % 81 / 9, j = (_ - 1) % 9;
sdk[i][j] = k;
}
int now_score = 0;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j) now_score += score[i][j] * sdk[i][j];
max_score = max(max_score, now_score);
};
auto &&[flag, res] = DLX(maps, true).dance(func);
cout << max_score;
return 0;
}