題意:一個全白的網格,你要將一些格子塗黑,每次只能選一行或一列中的連續白格塗黑,問最小操作次數
先假裝我們一次塗一個聯通塊,那麼答案就是聯通塊個數,然後在這個基礎上增加一些代價讓方案變得合法
考慮這樣建圖:對兩個水平相鄰的要塗黑的格子$x,y$,建新點$u$,連$(S,u,1),(u,x,\infty),(u,y,\infty)$,對兩個豎直相鄰的要塗黑的格子$x,y$,建新點$v$,連$(x,v,\infty),(y,v,\infty),(v,T,1)$
如果存在一條$S\rightarrow u\rightarrow x\rightarrow v\rightarrow T$的路徑,說明當前的塗色方案並不合法(染色轉彎),於是我們需要把這條路徑割掉,代表從聯通塊中分出一部分讓答案變得合法,所以答案還要加上這個圖的最小割
#include<stdio.h> #include<string.h> #include<algorithm> #include<vector> #include<string> using namespace std; const int inf=2147483647; int h[7510],nex[40010],to[40010],cap[40010],M=1,S,T; void ins(int a,int b,int c){ M++; to[M]=b; cap[M]=c; nex[M]=h[a]; h[a]=M; } void add(int a,int b,int c){ ins(a,b,c); ins(b,a,0); } int dis[7510],q[7510]; bool bfs(){ int head,tail,x,i; memset(dis,-1,sizeof(dis)); head=tail=1; q[1]=S; dis[S]=0; while(head<=tail){ x=q[head++]; for(i=h[x];i;i=nex[i]){ if(cap[i]&&dis[to[i]]==-1){ dis[to[i]]=dis[x]+1; if(to[i]==T)return 1; q[++tail]=to[i]; } } } return 0; } int cur[7510]; int dfs(int x,int flow){ if(x==T)return flow; int us=0,i,t; for(i=cur[x];i&&flow;i=nex[i]){ if(cap[i]&&dis[to[i]]==dis[x]+1){ t=dfs(to[i],min(flow,cap[i])); cap[i]-=t; cap[i^1]+=t; us+=t; flow-=t; if(cap[i])cur[x]=i; } } if(us==0)dis[x]=-1; return us; } int dicnic(){ int ans=0; while(bfs()){ memcpy(cur,h,sizeof(h)); ans+=dfs(S,inf); } return ans; } int id[60][60]; class BoardPainting{ public: int minimalSteps(vector<string>mp){ int n,m,i,j,N,ans; n=mp.size(); m=mp[0].length(); N=2; S=1; T=2; ans=0; for(i=0;i<n;i++){ for(j=0;j<m;j++){ if(mp[i][j]=='#'){ id[i][j]=++N; ans++; } } } for(i=0;i<n;i++){ for(j=0;j<m;j++){ if(id[i][j]){ if(id[i+1][j]){ ans--; N++; add(id[i][j],N,inf); add(id[i+1][j],N,inf); add(N,T,1); } if(id[i][j+1]){ ans--; N++; add(S,N,1); add(N,id[i][j],inf); add(N,id[i][j+1],inf); } } } } return ans+dicnic(); } }; /* int main(){ BoardPainting cl; vector<string>v; char s[110]; for(scanf("%s",s);s[0]!='E';scanf("%s",s))v.push_back(s); printf("%d",cl.minimalSteps(v)); } */