思维 + 搜索
首先,如果你从四个角开始搜,对于每一个位置取最小值的话是不可做的。
所以我们明确思路:枚举起点 ( i , j ) (i,j) (i,j) ,考虑将所有需要走到的位置打上标记,记录所走过的节点个数即可。注意路径会有交叉,所以不能记忆化。时间复杂度 O ( n 4 ) O(n^4) O(n4) 。
void dfs(int x, int y, int dir) {
if (x < 1 || x > r || y < 1 || y > c || tot == inf || step[x][y] == 1)
return;
if (step[x][y] == -1) {
tot = inf;
return;
}
step[x][y] = -1; //标记正在走过的点
tot += 2;
dfs(x + dx[dir << 1], y + dy[dir << 1], dir);
if ((dir == 0 && mp[x][y] == 'N') || (dir == 1 && mp[x][y] == 'Z')) {
dfs(x + dx[1], y + dy[1], (mp[x + dx[1]][y + dy[1]] == 'N') ? 0 : 1);
} else {
dfs(x + dx[3], y + dy[3], (mp[x + dx[3]][y + dy[3]] == 'N') ? 1 : 0);
}
step[x][y] = 1;
}
我们来观察这个搜索过程,发现有一个方向是必须走的,即 ( x + d x [ d i r < < 1 ] , y + d y [ d i r < < 1 ] , d i r ) (x+dx[dir<<1],y+dy[dir<<1],dir) (x+dx[dir<<1],y+dy[dir<<1],dir) ,而且方向也是固定的。
那么思路就很清晰了,我们可以直接继承上一状态所遍历到的所有位置,只需要改变一下枚举顺序:
for (int j = 1; j <= c; j++) {
for (int k = 1; k <= r; k++) {
for (int l = 1; l <= c; l++) {
step[k][l] = 0;
}
}
tot = 0;
for (int i = 1; i <= r; i++) {
dfs(i, j, 0);
ans[i][j] = min(ans[i][j], tot);
}
for (int k = 1; k <= r; k++) {
for (int l = 1; l <= c; l++) {
step[k][l] = 0;
}
}
tot = 0;
for (int i = r; i >= 1; i--) {
dfs(i, j, 1);
ans[i][j] = min(ans[i][j], tot);
}
}
时间复杂度 O ( n 3 ) O(n^3) O(n3)。至此,我们正确地解决了这个问题。