【洛谷】P2598 [ZJOI2009]狼和羊的故事(最小割)
阿新 • • 發佈:2021-10-28
題意
給定一張 \(n \times m\) 的網格圖,每個點可能是狼的領地,也可能是羊的領地,或者兩者皆不是。要求在一些點的邊上建柵欄,使得所有狼的領地和羊的領地無法直接相連。求最小建的柵欄數。
資料範圍
\(1 \leq n,m \leq 100\)。
思路
注意到題目要求羊的領地和狼的領地不連通,又要代價最小,可以聯絡到網路流中的最小割模型。
首先,從源點向所有羊的領地連一條流量為 \(+\infty\) 的邊,從所有狼的領地向匯點連一條流量為 \(+\infty\)。這樣可以保證在最小割中的割邊不會與源點或者匯點相連,在本題中的含義就是不會在不存在的地方建柵欄。再從所有原圖中的點向四周連一條流量為 \(1\)
這樣網路流模型就建好了。再根據最大流最小割定理,求出網路中的最大流就是本題的答案。
code:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+10; const int M=2e5+10; const int INF=0x3f3f3f3f; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; int h[N],idx=1,q[N],d[N],s,t,cur[N],n,m; struct edge{ int v,w,nex; }e[M]; void add(int u,int v,int w){e[++idx].v=v;e[idx].w=w;e[idx].nex=h[u];h[u]=idx;} bool bfs() { int hh=0,tt=-1;memset(d,-1,sizeof(d));d[s]=0,cur[s]=h[s];q[++tt]=s; while(hh<=tt) { int u=q[hh++]; for(int i=h[u];i;i=e[i].nex) { int v=e[i].v; if(d[v]==-1&&e[i].w) { d[v]=d[u]+1;cur[v]=h[v]; if(v==t) return true; q[++tt]=v; } } } return false; } int dfs(int u,int limit) { if(u==t) return limit; int flow=0; for(int i=cur[u];i&&flow<limit;i=e[i].nex) { int v=e[i].v;cur[u]=i; if(d[v]==d[u]+1&&e[i].w) { int t=dfs(v,min(e[i].w,limit-flow)); if(!t) d[v]=-1;flow+=t;e[i].w-=t,e[i^1].w+=t; } } return flow; } int dinic() { int res=0,flow; while(bfs()) while(flow=dfs(s,INF)) res+=flow; return res; } int get(int i,int j){return (i-1)*m+j;} int main() { scanf("%d%d",&n,&m);s=0,t=n*m+1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int opt,u=get(i,j),v;scanf("%d",&opt); if(opt==1) add(s,u,INF),add(u,s,0); if(opt==2) add(u,t,INF),add(t,u,0); for(int k=0;k<4;k++) { int x=i+dx[k],y=j+dy[k]; if(x<1||x>n||y<1||y>m) continue; v=get(x,y);add(u,v,1),add(v,u,0); } } printf("%d\n",dinic()); return 0; }