【题解】[BalticOI 2010 Day2] Mines
给定一个 \(N\times M\) 的矩阵 \(A\),你需要构造一个 \(0/1\) 矩阵 \(B\),满足 \(A_{i,j}\) 恰好等于它自己和相邻 \(8\) 个位置的 \(B\) 的和。
很适合比赛的一道提交答案题。
我们发现非常类似扫雷游戏,那么我们考虑从一个方向递推过去。
首先枚举左上角四个格子中的数,最多有 \(16\) 种情况,然后枚举第一行和第一列的数,那么剩下的位置可以被我们 \(N\times M\) 递推出来。
直接枚举是 \(2^{N+M}\) 的,\(N,M\) 非常大,这辈子都跑不完,同时这是提交答案题,我们可以考虑各种复杂度是不那么优秀的算法。
我们先写成搜索的构架,然后考虑慢慢优化。包括优化搜索顺序,我们按搜一行然后搜一列的方式交替搜索行和列。对于当前位置不合法及时返回。以及在枚举左上角后,预处理每一行和每一列的起始。
直接跑还是很慢,我们发现将地图旋转或翻转后答案本质不变,所以我们可以将地图转化为 \(8\) 个本质相同的地图,开多个程序同时跑即可。同时观察数据点,可以预处理下哪些行的 \(1\) 多,哪些列的 \(1\) 多。时间最长的点 \(6\) 号点也就跑了 \(5\) 分钟。个人认为 \(6\) 号点时间长是因为 \(N,M\) 的差比较大,并且 \(N,M\) 都 \(\ge 100\)。
以下是参考代码,但只用这一份代码是无法跑出所有测试点的。
#define N 605
int n, m, a[N][N], u[N][N], f[N][N]; char ch[N];
int r[N], c[N];
inline bool check(int x,int y,int op){
if(y > m || (x <= n && x <= y)){
u[x][1] = op, u[x][2] = r[x] - op;
if(u[x][2] < 0 || u[x][2] > 1)return false;
rep(i, 3, y - 1){
u[x][i] = a[x - 1][i - 1] - f[x - 1][i - 1] - u[x][i - 2] - u[x][i - 1] - u[x - 1][i] - u[x - 2][i];
if(u[x][i] < 0 || u[x][i] > 1)return false;
f[x][i] = u[x][i] + u[x][i - 1] + u[x - 1][i] + u[x - 1][i - 1];
if(x == n && f[x][i] + u[x][i - 2] + u[x - 1][i - 2] != a[x][i - 1])return false;
}
if(y > m && f[x][m] + u[x - 2][m] + u[x - 2][m - 1] != a[x - 1][m])return false;
}
else{
u[1][y] = op, u[2][y] = c[y] - op;
if(u[2][y] < 0 || u[2][y] > 1)return false;
rep(i, 3, x - 1){
u[i][y] = a[i - 1][y - 1] - f[i - 1][y - 1] - u[i - 2][y] - u[i - 1][y] - u[i][y - 1] - u[i][y - 2];
if(u[i][y] < 0 || u[i][y] > 1)return false;
f[i][y] = u[i][y] + u[i][y - 1] + u[i - 1][y] + u[i - 1][y - 1];
if(y == m && f[i][y] + u[i - 2][y] + u[i - 2][y - 1] != a[i - 1][y])return false;
}
if(x > n && f[n][y] + u[n][y - 2] + u[n - 1][y - 2] != a[n][y - 1])return false;
}
return true;
}
void dfs(int x,int y){
if(x > n && y > m){
if(f[n][m] == a[n][m]){
rp(i, n){rp(j, m)if(u[i][j])pc('X'); else pc('.'); el;}
exit(0);
}return ;
}
if(y > m || (x <= n && x <= y)){
if(check(x, y, 0))dfs(x + 1, y);
if(check(x, y, 1))dfs(x + 1, y);
}
else{
if(check(x, y, 0))dfs(x, y + 1);
if(check(x, y, 1))dfs(x, y + 1);
}
}
int main() {
read(n, m);
rp(i, n){
scanf("%s", ch + 1);
rp(j, m)a[i][j] = ch[j] - '0';
}
rep(s, 0, 15)if(a[1][1] == __builtin_popcount(s)){
u[1][1] = s & 1, u[1][2] = (s >> 1) & 1,
u[2][1] = (s >> 2) & 1, u[2][2] = (s >> 3) & 1;
r[1] = u[1][1] + u[1][2], r[2] = u[2][1] + u[2][2];
c[1] = u[1][1] + u[2][1], c[2] = u[1][2] + u[2][2];
bool flag = true;
rep(i, 3, n)r[i] = a[i - 1][1] - (a[i - 2][1] - r[i - 3]), flag &= r[i] >= 0 && r[i] <= 2;
rep(i, 3, m)c[i] = a[1][i - 1] - (a[1][i - 2] - c[i - 3]), flag &= c[i] >= 0 && c[i] <= 2;
if(!flag)continue;
rep(i, 2, n)f[i][2] = r[i] + r[i - 1];
rep(i, 2, m)f[2][i] = c[i] + c[i - 1];
if(a[n][1] == f[n][2] && a[1][m] == f[2][m])dfs(3, 3);
}
return 0;
}
/*
4 4
2332
3442
3331
3320
1 1 1 0
0 0 0 1
0 1 0 0
1 1 0 0
*/