BZOJ_4439_[Swerc2015]Landscaping_最小割
BZOJ_4439_[Swerc2015]Landscaping_最小割
Description
FJ有一块N*M的矩形田地,有两种地形高地(用‘#’表示)和低地(用‘.’表示)
FJ需要对每一行田地从左到右完整开收割机走到头,再对每一列从上到下完整走到头,如下图所示
对于一个4*4的田地,FJ需要走8次。
收割机是要油的,每次从高地到低地或从低地到高地需要支付A的费用。
但是FJ有黑科技,可以高地与低地的互变,都只需要一个支付B的费用。
询问FJ需要支付最小费用。
Input
第一行包含四个整数N,M,A,B,意义如上文所述。
接下来是一个N*M的字符串矩阵,表示农田的地形,’#’表示高地,’.’表示低地。
Output
只包含一个正整数,表示最小费用。
1<=N,M<=50
1<=A,B<=100000
1<=A,B<=100000
Sample Input
5 4 1000 2000
...#
#..#
...#
##..
###.
...#
#..#
...#
##..
###.
Sample Output
11000
样例解释:
把(2,1)的高地变成低地花费2000,燃料花费9000
用最小割的思想。
S->高地(B) 低地->T(B),割这些边表示一开始将高低互换。
然后对于每个点向四周连边(A),割这个表示支付A从而改变与四周的联系,可以理解为边是双向的。
求最小割即可。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 3050 #define M 300050 #define S (n*m+1) #define T (n*m+2) #define inf 100000000 #define p(i,j) ((i-1)*m+j) int head[N],to[M],nxt[M],flow[M],cnt=1,n,m,dep[N],Q[N],l,r; inline void add(int u,int v,int f) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; flow[cnt]=f; to[++cnt]=u; nxt[cnt]=head[v]; head[v]=cnt; flow[cnt]=0; } bool bfs() { memset(dep,0,sizeof(dep)); dep[S]=1; l=r=0; Q[r++]=S; while(l<r) { int x=Q[l++],i; for(i=head[x];i;i=nxt[i]) { if(!dep[to[i]]&&flow[i]) { dep[to[i]]=dep[x]+1; if(to[i]==T) return 1; Q[r++]=to[i]; } } } return 0; } int dfs(int x,int mf) { if(x==T) return mf; int nf=0,i; for(i=head[x];i;i=nxt[i]) { if(dep[to[i]]==dep[x]+1&&flow[i]) { int tmp=dfs(to[i],min(mf-nf,flow[i])); if(!tmp) dep[to[i]]=0; nf+=tmp; flow[i]-=tmp; flow[i^1]+=tmp; if(nf==mf) break; } } return nf; } void dinic() { int ans=0,f; while(bfs()) while(f=dfs(S,inf)) ans+=f; printf("%d\n",ans); } char s[60]; int main() { int A,B,i,j; scanf("%d%d%d%d",&n,&m,&A,&B); for(i=1;i<=n;i++) { scanf("%s",s+1); for(j=1;j<=m;j++) { if(s[j]=='#') { add(S,p(i,j),B); }else { add(p(i,j),T,B); } } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(i>1) add(p(i,j),p(i-1,j),A); if(i<n) add(p(i,j),p(i+1,j),A); if(j>1) add(p(i,j),p(i,j-1),A); if(j<m) add(p(i,j),p(i,j+1),A); } } dinic(); }