P1074 靶形数独
题目理解:
- 和数独题目类似。行列,小九宫格不能重复。
- 有加权记分。
- 中间的分数高。
想法:
-
加权计分。不用开数组,只用 temp += mp[i]/[j]* min(10 - abs(i - 4), 10 - abs(j - 4));
-
for (int i = 0; i < 9; ++i) { for (int j = 0; j < 9; ++j) { temp += mp[i][j] * min(10 - abs(i - 4), 10 - abs(j - 4)); } }
-
超时问题。需要重新优化。不能从0-8行去填数。需要从最好填写的行下手。最好下手就是那行空最少的行。
-
将空的数量从多到少排列。就找到了填数的行的顺序。这里需要利用结构体来排序。
代码:
//P1074 靶形数独
// Created by majoe on 2020/4/29.
//https://www.luogu.com.cn/problem/P1074
//和数独1784类似
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10;
//cx,cy,cblock记录行,列,小九宫格出现的数字
int mp[maxn][maxn],cx[maxn][maxn],cy[maxn][maxn],cblock[maxn][maxn];
//如果数独无解,就输出-1
int ans=-1;
/**
* 原解法超时,只有通过优化。优化方法如下:
* 以前是从第0-8行来填数,现在先填好填的,就是空少的行。
* 因此需要设置结构体,用于排序。
*/
struct Node{
int num, cnt;
}so[9];
//从大到小排列
bool cmp(Node n1 ,Node n2){
return n1.cnt > n2.cnt;
}
void dfs(int x, int y) {
//x实际是指在完成第x+1行,但是具体第几行,要看so[sx].num
int sx = x;
//具体的行号
x = so[sx].num;
//搞定了9行,自然得输出
if (sx == 9) {
int temp = 0;
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
temp += mp[i][j] * min(10 - abs(i - 4), 10 - abs(j - 4));
}
}
ans = max(temp,ans);
return;
}
//换行
if (y == 9) {
dfs(sx + 1, 0);
return;
}
//如果mp上有数,向右前进,填下一个
if (mp[x][y]) {
dfs(sx, y + 1);
} else { // 如果x,y是0
for (int i = 1; i <= 9; ++i) {
//如果占位数组没有被占据
if (!cx[x][i] && !cy[y][i] && !cblock[x / 3 * 3 + y / 3][i]) {
cx[x][i] = 1;
cy[y][i] = 1;
cblock[x / 3 * 3 + y / 3][i] = 1;
mp[x][y] = i;
dfs(sx, y + 1);
//回溯
mp[x][y] = 0;
cx[x][i] = 0;
cy[y][i] = 0;
cblock[x / 3 * 3 + y / 3][i] = 0;
}
}
}
return;
}
int main() {
for (int i = 0; i < 9; ++i) {
so[i].num = i;
for (int j = 0; j < 9; ++j) {
cin >> mp[i][j];
//如果格子中有数,就将占位的数组填上
if (mp[i][j]) {
//so是记录每行上初始的数字个数。
so[i].cnt++;
cx[i][mp[i][j]] = 1;
cy[j][mp[i][j]] = 1;
cblock[i / 3 * 3 + j / 3][mp[i][j]] = 1;
}
}
}
//从最好填的行开始填。其实就是枚举所有的可能
//让初始化数字多的行,排在前面
sort(so,so+9,cmp);
dfs(0, 0);
cout << ans;
return 0;
}