题目:BZOJ1412、洛谷P2598、Vijos P1555、codevs2351。
题目大意:有一个nm矩阵,每格里住着狼、羊或其他动物。现在要你建最少的篱笆,使得狼和羊分开。问最少建多长的篱笆。
解题思路:网络流最小割问题,求最大流即可。
首先建超级源点S=0,超级汇点T=nm+1。对于每只狼,从S到这只狼连接容量inf的边;对于每只羊,从这只羊到T连接容量inf的边。
然后,对于每个点,若该点不是羊,则从该点向它四个方向所有不是狼的点连接容量为1的边。
建完图后就是裸的最大流问题,用Dinic、ISAP等都可,EK应该也可。
我用的是Dinic。
C++ Code:
#include<stdio.h> #include<cctype> #include<vector> #include<algorithm> #include<cstring> #include<queue> using namespace std; #define INF 0x3f3f3f3f int n,m,level[30003],iter[30003],a[103][103]; struct edges{ int to,cap,rev; }; vector<edges>G[200003]; queue<int>q; inline int readint(){ int p=0; char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar())p=p*10+c-'0'; return p; } inline void addedge(int from,int to,int cap){ G[from].push_back((edges){to,cap,G[to].size()}); G[to].push_back((edges){from,0,G[from].size()-1}); } void bfs(int s){ memset(level,-1,sizeof(level)); level[s]=0; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<G[u].size();++i){ edges& e=G[u][i]; if(level[e.to]<0&&e.cap>0){ level[e.to]=level[u]+1; q.push(e.to); } } } } int dfs(int u,int t,int f){ if(u==t)return f; for(int& i=iter[u];i<G[u].size();++i){ edges& e=G[u][i]; if(e.cap>0&&level[e.to]>level[u]){ int d=dfs(e.to,t,min(f,e.cap)); if(d){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int max_flow(int s,int t){ int flow=0; while(1){ bfs(s); if(level[t]<0)return flow; memset(iter,0,sizeof(iter)); int f; while(f=dfs(s,t,INF))flow+=f; } } int main(){ memset(a,-1,sizeof a); n=readint(),m=readint(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)a[i][j]=readint(); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ if(a[i][j]==1)addedge(0,(i-1)*m+j,INF);else if(a[i][j]==2) addedge((i-1)*m+j,n*m+1,INF); if(a[i][j]==2)continue; if(a[i+1][j]==2||a[i+1][j]==0)addedge((i-1)*m+j,i*m+j,1); if(a[i-1][j]==2||a[i-1][j]==0)addedge((i-1)*m+j,(i-2)*m+j,1); if(a[i][j+1]==2||a[i][j+1]==0)addedge((i-1)*m+j,(i-1)*m+j+1,1); if(a[i][j-1]==2||a[i][j-1]==0)addedge((i-1)*m+j,(i-1)*m+j-1,1); } printf("%d ",max_flow(0,n*m+1)); return 0; }