很厉害的题啊。差点就做出来了。
首先看到题意之后会有一个网络流想法:将网格中每个节点,向四个方向连流量为
的边, 连向蓝色接口,红色接口连向 ,图中最大流就是答案。
直接最大流显然做不得,考虑将最大流转化为最小割。通过割最少的边来让红蓝不连通。
我们观察割的过程,首先肯定不会存在一个接口既不与 连通也不与 连通,所以每种接口可以按与 连与
连分成两种颜色,我们也称其为蓝和红,这样,如果原来的某个红色接口与
之间的边被割了,我们就认为它改变了颜色,花费
的代价。网格图中间的节点,成为哪种颜色都没代价。那么此时的蓝色和红色,就分别指代与
和与 连通的点集。
观察两个颜色不同的图中节点,我们在其中间连一条割线,那么割线组成的所有路径就把平面分成了若干块,代价为割线长度总和。我们观察割线组成的路径有什么性质。
首先肯定不会有封闭的环,这个是很显然的,要不然我们可以直接把环中的颜色改变,这一步没代价,但是这个环没了,代价减小。
其次,肯定不会有连接同一个边界上两个点或者相邻边界上两点的路径,否则也可以把中间颜色全部改变,把路径消掉,手玩易得代价变化量不超过
,不会增大。
最后也是最关键的性质:任何一条割线组成的路径不会拐弯。
如果拐弯了完全可以掰直,代价也不会变。
也就是说,割线只可能从第
行的左端点连向第
行的右端点,或者第 列上端点连向第
列下端点。
那么我们可以分类讨论是连行还是连列,以连行为例。设 表示做到第 行,第 行颜色为蓝/红的最小代价,容易从 转移到 。这样 H1 就可以
做完了。
至于 H2,因为 DP 的第二维很小,用线段树动态维护转移矩阵即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int n,m,Q; char U[N],D[N],L[N],R[N]; int dp[N][2]; int main() { cin >> n >> m >> Q; cin >> (L + 1); cin >> (R + 1); cin >> (U + 1); cin >> (D + 1); int ans = 1e9; for(int i = 0;i <= m;i++) dp[i][0] = dp[i][1] = 1e9; dp[1][0] = (U[1] != 'B') + (D[1] != 'B'); dp[1][1] = (U[1] != 'R') + (D[1] != 'R'); for(int i = 1;i <= n;i++) dp[1][0] += (L[i] != 'B'),dp[1][1] += (L[i] != 'R'); for(int i = 2;i <= m;i++) { dp[i][0] = min(dp[i - 1][0],dp[i - 1][1] + n) + (U[i] != 'B') + (D[i] != 'B'); dp[i][1] = min(dp[i - 1][1],dp[i - 1][0] + n) + (U[i] != 'R') + (D[i] != 'R'); } for(int i = 1;i <= n;i++) dp[m][0] += (R[i] != 'B'),dp[m][1] += (R[i] != 'R'); ans = min({ans,dp[m][0],dp[m][1]}); for(int i = 0;i <= n;i++) dp[i][0] = dp[i][1] = 1e9; dp[1][0] = (L[1] != 'B') + (R[1] != 'B'); dp[1][1] = (L[1] != 'R') + (R[1] != 'R'); for(int i = 1;i <= m;i++) dp[1][0] += (U[i] != 'B'),dp[1][1] += (U[i] != 'R'); for(int i = 2;i <= n;i++) { dp[i][0] = min(dp[i - 1][0],dp[i - 1][1] + m) + (L[i] != 'B') + (R[i] != 'B'); dp[i][1] = min(dp[i - 1][1],dp[i - 1][0] + m) + (L[i] != 'R') + (R[i] != 'R'); } for(int i = 1;i <= m;i++) dp[n][0] += (D[i] != 'B'),dp[n][1] += (D[i] != 'R'); ans = min({ans,dp[n][0],dp[n][1]}); cout << ans << endl; return 0; }
|