题意
有一片h*w的草坪,要把每一行从左到右修剪一遍,每一列从上到下修剪一遍。每个草坪要么是高低要么是平地。割草机从高地到平地或者从平地到高地,需要花费a。也可以把平地变为高地或者把高地变为平地,花费为b。求出最小花费是多少。
分析
网络流,应该也不算网络流里的难题,建图还是比较好想的(虽然我不会)。
当时在场上瞎几把建图,最后还是没过
这场结束后问了一下二发学长,恍然大悟。
把草地分为两个集合S和T,平地为S集合,高地位T集合,然后用最少的花费将两个集合分开,那么就是最小割了。
从s(源点)向每个平地连一条容量为b的边,从每个高地向t(汇点)连一条容量为b的边。如果需要把平地变高地或者把高低变平地,就割这几条边就可以了。
每个小草坪都向左边和下面的小草坪连双向边。为什么是双向边?因为是按照每个小草地的性质分为的两大集合。也可以理解为有可能从平地开始,也有可能从高地开始。
然后跑dinic就可以惹~~
下面是代码~
其实没啥看的,建图很简单,dinic是网上的模板··雾
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <vector> 6 #include <queue> 7 using namespace std; 8 9 #define N 5000 10 #define INF 2147483000 11 12 struct Edge 13 { 14 int from,to,cap,flow; 15 Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){} 16 }; 17 18 struct Dinic 19 { 20 int n,m,s,t;//结点数,边数(包括反向弧),源点编号,汇点编号 21 vector<Edge>edges;//边表,dges[e]和dges[e^1]互为反向弧 22 vector<int>G[N];//邻接表,G[i][j]表示结点i的第j条边在e数组中的编号 23 bool vis[N]; //BFS的使用 24 int d[N]; //从起点到i的距离 25 int cur[N]; //当前弧下标 26 27 void addedge(int from,int to,int cap) 28 { 29 edges.push_back(Edge(from,to,cap,0)); 30 edges.push_back(Edge(to,from,0,0)); 31 int m=edges.size(); 32 G[from].push_back(m-2); 33 G[to].push_back(m-1); 34 } 35 36 bool bfs() 37 { 38 memset(vis,0,sizeof(vis)); 39 queue<int>Q; 40 Q.push(s); 41 d[s]=0; 42 vis[s]=1; 43 while(!Q.empty()) 44 { 45 int x=Q.front();Q.pop(); 46 for(int i=0;i<G[x].size();i++) 47 { 48 Edge&e=edges[G[x][i]]; 49 if(!vis[e.to]&&e.cap>e.flow)//只考虑残量网络中的弧 50 { 51 vis[e.to]=1; 52 d[e.to]=d[x]+1; 53 Q.push(e.to); 54 } 55 } 56 57 } 58 return vis[t]; 59 } 60 61 int dfs(int x,int a)//x表示当前结点,a表示目前为止的最小残量 62 { 63 if(x==t||a==0)return a;//a等于0时及时退出,此时相当于断路了 64 int flow=0,f; 65 for(int&i=cur[x];i<G[x].size();i++)//从上次考虑的弧开始,注意要使用引用,同时修改cur[x] 66 { 67 Edge&e=edges[G[x][i]];//e是一条边 68 if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0) 69 { 70 e.flow+=f; 71 edges[G[x][i]^1].flow-=f; 72 flow+=f; 73 a-=f; 74 if(!a)break;//a等于0及时退出,当a!=0,说明当前节点还存在另一个曾广路分支。 75 76 } 77 } 78 return flow; 79 } 80 81 int Maxflow(int s,int t)//主过程 82 { 83 this->s=s,this->t=t; 84 int flow=0; 85 while(bfs())//不停地用bfs构造分层网络,然后用dfs沿着阻塞流增广 86 { 87 memset(cur,0,sizeof(cur)); 88 flow+=dfs(s,INF); 89 } 90 return flow; 91 } 92 }dinic; 93 int h,w,a,b; 94 char G[55][55]; 95 const int dx[]={0,0,1,-1}; 96 const int dy[]={1,-1,0,0}; 97 int main(){ 98 scanf("%d%d%d%d",&h,&w,&a,&b); 99 dinic.s=0,dinic.t=h*w+1; 100 101 for(int i=1;i<=h;i++){ 102 for(int j=1;j<=w;j++){ 103 scanf(" %c",&G[i][j]); 104 if(G[i][j]=='.') 105 dinic.addedge(dinic.s,(i-1)*w+j,b); 106 else 107 dinic.addedge((i-1)*w+j,dinic.t,b); 108 } 109 } 110 for(int i=1;i<=h;i++){ 111 for(int j=1;j<=w;j++){ 112 int u=(i-1)*w+j; 113 int v1=i*w+j; 114 int v2=(i-1)*w+j+1; 115 if(i<h){ 116 dinic.addedge(u,v1,a);dinic.addedge(v1,u,a); 117 } 118 if(j<w){ 119 dinic.addedge(u,v2,a);dinic.addedge(v2,u,a); 120 } 121 } 122 } 123 int ans=dinic.Maxflow(dinic.s,dinic.t); 124 cout<<ans; 125 return 0; 126 }