CF1368H1 Breadboard Capacity
阿新 • • 發佈:2021-08-10
一、題目
\(\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); }