[CF1368H1]Breadboard Capacity (easy version)
阿新 • • 發佈:2020-10-14
題目
點這裡看題目。
分析
首先,不難發現此題可以方便地建出網路流的圖來。圖中的每個節點向周圍四個點連一條容量為 1 的無向邊,然後 \(S\) 連向紅色介面, \(T\) 連向藍色介面。
原題的答案便是此圖上的最大流。顯然原問題沒法做,我們直接上最小割。
最小割本質上就是要將點拆分成兩個點集。為了方便,我們就將 \(S\) 集合中的點染成紅色, \(T\) 集合中的染成藍色。
那麼原圖中的最小割就等於兩端點顏色不同的邊的數量。
考慮內部的 \(n\times m\) 個點應該如何最小化這種邊的數量。仍然是為了方便,我們給異色端點的邊上劃一道垂直的虛線,考慮這樣的虛線連為的路徑。
如上圖。假如電路板的內部出現了一個環,很顯然我們應該改變內部點的顏色,消除它。這樣答案不會變劣。
如上圖。假如電路板內部出現了連線相鄰的兩個邊界或者同一個邊界的路徑,我們顯然應該改變它們的顏色,消除它們。這樣答案不會變劣。
如上圖。假如電路板內部出現了連線相對的兩個邊界的路徑,我們同樣可以改變它們的顏色,並且把它們拉直,使得它們在非邊界的位置不拐彎。
因此,這樣操作之後,圖上就只會在列上或者行上存在連線相對邊界的不拐彎的路徑。
行列本身是類似的,單獨考慮列。最終的最優情況就是所有列的顏色相同。
這個問題就可以用一個簡單的 DP 來處理了:
\(f(i,0/1)\):前 \(i\) 行,顏色為 \(0\) (表示藍色)或者 \(1\) (表示紅色)時的最小割。
轉移略。對行和列都做一遍,對所有的情況取 \(\min\)
關於 H2 ,由於 DP 的第二位很小,因此可以用矩陣來表示轉移。這樣就可以用線段樹維護轉移矩陣,單次修改是 \(O(\log_2n)\) 的。
小結:
- 將問題建出對應的網路來,並且將最大流轉換成最小割,將最小割轉換成染色問題。
- 將染色問題轉化為了類似於周長的問題,壓縮最優解的情況。
程式碼
#include <cstdio> #include <iostream> using namespace std; const int MAXN = 1e5 + 5; template<typename _T> void read( _T &x ) { x = 0; char s = getchar(); int f = 1; while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); } while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); } x *= f; } template<typename _T> void write( _T x ) { if( x < 0 ) putchar( '-' ), x = -x; if( 9 < x ) write( x / 10 ); putchar( x % 10 + '0' ); } template<typename _T> _T MIN( const _T a, const _T b ) { return a < b ? a : b; } char S[MAXN]; int up[MAXN], down[MAXN], lef[MAXN], rig[MAXN]; int N, M, Q; void Get( const int n, int *a ) { scanf( "%s", S + 1 ); for( int i = 1 ; i <= n ; i ++ ) a[i] = S[i] == 'R'; } int Calc() { int f0 = 0, f1 = 0, n0, n1; for( int i = 1 ; i <= M ; i ++ ) f0 += up[i], f1 += ! up[i]; for( int i = 1 ; i <= N ; i ++ ) { n0 = MIN( f0, f1 + M ) + lef[i] + rig[i]; n1 = MIN( f0 + M, f1 ) + ( ! lef[i] ) + ( ! rig[i] ); f0 = n0, f1 = n1; } for( int i = 1 ; i <= M ; i ++ ) f0 += down[i], f1 += ! down[i]; return MIN( f0, f1 ); } int main() { read( N ), read( M ), read( Q ); Get( N, lef ), Get( N, rig ); Get( M, up ), Get( M, down ); int ans1 = Calc(), ans2; swap( N, M ), swap( up, lef ), swap( down, rig ); ans2 = Calc(); write( MIN( ans1, ans2 ) ), putchar( '\n' ); return 0; }