1. 程式人生 > 實用技巧 >[CF1368H1]Breadboard Capacity (easy version)

[CF1368H1]Breadboard Capacity (easy version)

題目

點這裡看題目。

分析

首先,不難發現此題可以方便地建出網路流的圖來。圖中的每個節點向周圍四個點連一條容量為 1 的無向邊,然後 \(S\) 連向紅色介面, \(T\) 連向藍色介面。

原題的答案便是此圖上的最大流。顯然原問題沒法做,我們直接上最小割

最小割本質上就是要將點拆分成兩個點集。為了方便,我們就將 \(S\) 集合中的點染成紅色\(T\) 集合中的染成藍色

那麼原圖中的最小割就等於兩端點顏色不同的邊的數量

考慮內部的 \(n\times m\) 個點應該如何最小化這種邊的數量。仍然是為了方便,我們給異色端點的邊上劃一道垂直的虛線,考慮這樣的虛線連為的路徑。

如上圖。假如電路板的內部出現了一個環,很顯然我們應該改變內部點的顏色,消除它。這樣答案不會變劣。

如上圖。假如電路板內部出現了連線相鄰的兩個邊界或者同一個邊界的路徑,我們顯然應該改變它們的顏色,消除它們。這樣答案不會變劣。

如上圖。假如電路板內部出現了連線相對的兩個邊界的路徑,我們同樣可以改變它們的顏色,並且把它們拉直,使得它們在非邊界的位置不拐彎。

因此,這樣操作之後,圖上就只會在列上或者行上存在連線相對邊界的不拐彎的路徑

行列本身是類似的,單獨考慮列。最終的最優情況就是所有列的顏色相同

這個問題就可以用一個簡單的 DP 來處理了:

\(f(i,0/1)\):前 \(i\) 行,顏色為 \(0\) (表示藍色)或者 \(1\) (表示紅色)時的最小割。

轉移略。對行和列都做一遍,對所有的情況取 \(\min\)

就是最小割,即答案。

關於 H2 ,由於 DP 的第二位很小,因此可以用矩陣來表示轉移。這樣就可以用線段樹維護轉移矩陣,單次修改是 \(O(\log_2n)\) 的。

小結:

  1. 將問題建出對應的網路來,並且將最大流轉換成最小割,將最小割轉換成染色問題
  2. 染色問題轉化為了類似於周長的問題,壓縮最優解的情況

程式碼

#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;
}