[COCI2019-2022#1] Zoo

lc0802發表於2024-05-24

[COCI2019-2022#1] Zoo

$${\Huge{\odot}}$$

題意

現在你有一個 \(N \times M\) 的網格,其中一開始全部都是白色的。現在每次可以選擇一個四聯通的聯通塊,其中包含左上角和右上角,並且染成 B 顏色和 T 顏色。現在給定最終狀態,求最少需要幾次操作才能達到改狀態。\(1 \le N, M \le 1000\)

題解

首先容易會發現,最後一次填的顏色一定是起點和終點的顏色,並且他們是在同一聯通塊裡面。也很容易想到,兩個相鄰聯通塊的顏色不同。如果聯通塊 \(A\)\(B, C\) 相鄰,首先可得 \(B, C\) 不相鄰。並且如果確定聯通塊 \(A\)\(B, C\) 染,就可以知道 \(B, C\) 一起染最優。在這一次的染色中只要同時對 \(A \cap B \cap C\) 的位置染色,然後再單獨染 \(A\) 即可。

於是最佳的策略應該是最後一步染起點和終點的聯通塊,之前染與其相鄰的,在之前染與倒數第 \(2\) 次相鄰的等等。那麼只需要進行 DFS 記憶化搜尋即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 2005;
int dx[] = {0, 0, -1, 1};
int dy[] = {-1, 1, 0, 0};
int n, m, ans;
char c[N][N];
map<pair<int, int>, bool> used;
priority_queue<pair<int, pair<int, int> > > q;

signed main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			cin >> c[i][j];
	
	used[make_pair(1, 1)] = 1;
	q.push(make_pair(0, make_pair(1, 1)));
	while(!q.empty()){
		int color = -q.top().first;
		int x = q.top().second.first, y = q.top().second.second;
		ans = max(ans, color);
		q.pop();
		for(int i = 0; i < 4; i++){
			int vx = x + dx[i], vy = y + dy[i], vc = color;
			if(vx < 1 || vx > n || vy < 1 || vy > m) continue;
			if(c[vx][vy] == '*') continue;
			if(used[make_pair(vx, vy)]) continue;
			used[make_pair(vx, vy)] = 1;
			if(c[x][y] != c[vx][vy]) vc++;
			q.push(make_pair(-vc, make_pair(vx, vy)));
		}
	}
	cout << ans + 1 << endl;
	return 0;
}