[ZJOI2009] 狼和羊的故事
阿新 • • 發佈:2021-07-07
題目大意
給定一個 \(nm\) 的由 \((0,1,2)\) 組成的矩陣,要求在兩個數之間建牆,使得任意兩個 \(1\) 和 \(2\) 不連通(兩個數相鄰且之間無牆即為聯通),求最少要建多少牆。
這道題可視作是要將 \(1\) 和 \(2\) 劃分成兩個集合,考慮最小割。
\(s\longrightarrow\{(i,j)|mp_{i,j}==1\}\),容量 \(+\infty\)。
\(t\longrightarrow\{(i,j)|mp_{i,j}==2\}\),容量 \(+\infty\)。
容量 \(+\infty\) 是為了保證最終不會出現 \(1\) 和 \(2\) 在一個集合內的情況。
由於每個格子和上下左右四個格子相鄰,所以將它和周圍的格子連起來。
\((i,j)\longrightarrow(i+1,j)\),容量為 \(1\)。
\((i,j)\longrightarrow(i,j+1)\),容量為 \(1\)。
\((i,j)\longrightarrow(i-1,j)\),容量為 \(1\)。
\((i,j)\longrightarrow(i,j-1)\),容量為 \(1\)。
這樣,跑最小割時,每割掉一條邊,就相當於在這兩個格子之間建一堵牆,代價 \(++\) ,所以上面建圖時容量為 \(1\) 。
最終最小割即為答案。
Code
#include<bits/stdc++.h> //#define int long long #define pair pair<int,int> using namespace std; inline void end() { puts(""); system("pause"); } inline int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*f; } const int N=1e4+4,M=5e5+5; int n,m,s,t,ans; int first[N],nex[M],to[M],w[M],num=1; inline void add(int u,int v,int val) { nex[++num]=first[u]; first[u]=num; to[num]=v; w[num]=val; } inline void Add(int u,int v,int val) { add(u,v,val); add(v,u,0); } namespace ISAP { int dep[N],gap[N],cur[N]; void bfs() { memset(dep,-1,sizeof(dep)); memset(gap,0,sizeof(gap)); queue<int> q; q.push(t); dep[t]=0;gap[0]=1; while(!q.empty()) { int u=q.front();q.pop(); for(int i=first[u];i;i=nex[i]) { int v=to[i]; if(dep[v]!=-1) continue; dep[v]=dep[u]+1; gap[dep[v]]++; q.push(v); } } } inline int dfs(int u,int in) { if(u==t) return in; int out=0; for(int i=cur[u];i;i=nex[i]) { cur[u]=i; int v=to[i]; if(!w[i]||dep[v]!=dep[u]-1) continue; int res=dfs(v,min(w[i],in-out)); w[i]-=res; w[i^1]+=res; out+=res; if(in==out) return out; } gap[dep[u]]--; if(!gap[dep[u]]) dep[s]=n*m+3; dep[u]++; gap[dep[u]]++; return out; } void work() { bfs(); while(dep[s]<n*m+2) { memcpy(cur,first,sizeof(first)); ans+=dfs(s,1e9); } } } int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0}; inline int id(int x,int y){return (x-1)*m+y;} int main() { n=read(),m=read(); s=0,t=n*m+1; for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { int x=read(); if(x==1) Add(s,id(i,j),1e9); else if(x==2) Add(id(i,j),t,1e9); for(int k=0;k<4;++k) { int xx=i+dx[k],yy=j+dy[k]; if(xx<1||yy<1||xx>n||yy>m) continue; Add(id(i,j),id(xx,yy),1); } } } ISAP::work(); printf("%d",ans); //end(); return 0; }