P6370 [COCI2006-2007#6] KAMEN 题解

P6370

暴力是很容易的,可以直接搜索。

发现可以直接对暴力进行优化。每次有一个地方变为 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;
}
posted @ 2023-12-27 10:21  Pengzt  阅读(3)  评论(0编辑  收藏  举报