1. 程式人生 > 其它 >CF1368H1 Breadboard Capacity

CF1368H1 Breadboard Capacity

一、題目

點此看題

\(\tt H2\) 有點毒瘤,不是很想寫。

二、解法

首先對原問題建出網路流圖,我們把 \(S\) 連所有藍色介面,\(T\) 連所有紅色介面,矩形內的所有點也建出來,向四周連容量為 \(1\) 的無向邊,然後對原圖跑最大流就是答案。

這裡補充一個小知識點,也就是網路流圖怎麼連無向邊,舉例:\((u,v)\) 之間一條容量為 \(1\) 的邊。

拆分成兩條邊,\(u\rightarrow v\)\(v\rightarrow u\) 分別連容量為 \(1\) 的互補邊,如果我們流 \(u\rightarrow v\) 那麼 \(v\rightarrow u\) 會增加一點流量,那麼以後流 \(v\rightarrow u\)

的時候相當於交換這兩條路徑的終點,這條無向邊恢復了原來的狀態,完成了反悔操作。

肯定不能直接跑最大流,有一個套路是求最小割,再轉化一下就是把矩陣的所有點染藍或者染紅(代表最後和起點還是和終點聯通),求所有染色方案下的最小端點異色邊數。

直接 \(dp\) 根本不行,我們直接構造最優解算了。先考慮整張圖都是紅色,那麼得到的割是藍色點數,我們考慮把矩形中的一些點改成藍色使得割更小,首先改點肯定要有點挨著邊界,然後考慮要改肯定是改一整行或者一整列,我覺得很顯然。

那麼得到結論:最優染色方案一定是一整行或者一整列顏色相同。

所以先對行 \(dp\) 一次,再把整個矩陣翻轉對列也 \(dp\) 一次即可,時間複雜度 \(O(n)\)

三、總結

要敢於考慮高複雜度做法,一開始把矩形中的每個點都建在網路流裡面我是真的沒想到,只要你敢想就有優化的可能。

手算最小割是常見套路,通常是考慮每個點屬於 \(S\) 或者屬於 \(T\) 來考慮染色問題。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,q,l[M],r[M],u[M],d[M];char s[M];
void get(int n,int *a)
{
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
		a[i]=(s[i]=='R');
}
int cal()
{
	int f0=0,f1=0,g0,g1;
	for(int i=1;i<=m;i++)
		f0+=u[i],f1+=(!u[i]);
	for(int i=1;i<=n;i++)
	{
		g0=f0;g1=f1;
		f0=min(g0,g1+m)+l[i]+r[i];
		f1=min(g0+m,g1)+(!l[i])+(!r[i]);
	}
	for(int i=1;i<=m;i++)
		f0+=d[i],f1+=(!d[i]);
	return min(f0,f1);
}
signed main()
{
	n=read();m=read();q=read();
	get(n,l);get(n,r);get(m,u);get(m,d);
	int ans=cal();
	swap(n,m);swap(l,u);swap(r,d);
	ans=min(ans,cal());
	printf("%d\n",ans);
}