P6370 [COCI2006-2007#6] KAMEN 题解
暴力是很容易的,可以直接搜索。
发现可以直接对暴力进行优化。每次有一个地方变为 O
后,重构代价太大,设这个坐标为 \((i, j)\),同时维护一个类型为 pair<int, int>
的 \(to\) 数组表示将球放在第 \(i\) 列第一行的时候,最后到达的位置。再用一个 \(path_i\) 记录下从第一行第 \(i\) 列出发的路径以及一个 \(vis_{i, j, k}\) 表示第 \(i\) 行第 \(j\) 列是否能通过放在第 \(k\) 列到达,如果可以,记录的就是再 \(path\) 中的下标。然后发现只有 \((i, j)\) 附近的八个格子可能会受影响,剪枝一下,即判断 \((i, j)\) 之前是否在 \((i', j')\) 所经过的路径上即可。这样实际上就是根据路径上的前缀不变对暴力进行的优化。
为什么这样的时间复杂度是对的呢?因为每次 \(to_i\) 的行数要么增加 \(\Delta\),要么 \(-1\),所以均摊下来的时间复杂度就是对的了。
但是这样空间是 \(\mathcal{O}(rc^2)\),无法通过。发现每次的 O
所在的位置一定是所有路径的终点,所以就不用记录 \(vis\) 数组了,因为每次操作一定是 \(path\) 的栈顶,直接 pop
即可。
时间复杂度:\(\mathcal{O}(rc^2)\)。
空间复杂度:\(\mathcal{O}(rc)\)。
代码:
mt19937_64 rng(35);
constexpr int N = 3e4 + 10;
int n, m, q;
int tp[35], pa[35][N];
char ch[N][35];
void work(int x) {
while(1) {
int y = pa[x][tp[x]];
if(ch[tp[x]][y] == 'O') --tp[x];
else if(tp[x] == n) break;
else if(ch[tp[x] + 1][y] == 'X') break;
else if(ch[tp[x] + 1][y] == '.') pa[x][++tp[x]] = y;
else if(y > 1 && ch[tp[x]][y - 1] == '.' && ch[tp[x] + 1][y - 1] == '.') pa[x][++tp[x]] = y - 1;
else if(y < m && ch[tp[x]][y + 1] == '.' && ch[tp[x] + 1][y + 1] == '.') pa[x][++tp[x]] = y + 1;
else break;
}
ch[tp[x]][pa[x][tp[x]]] = 'O';
}
int main() {
ios :: sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
cin >> ch[i][j];
for(int i = 1; i <= m; ++i)
pa[i][tp[i] = 0] = i;
cin >> q;
for(int qi = 1; qi <= q; ++qi) {
int x;
cin >> x;
work(x);
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j)
cout << ch[i][j];
cout << "\n";
}
return 0;
}