1515 - Pool construction(最大流最小割模型)

来源:互联网 发布:淘宝天猫内购券 编辑:程序博客网 时间:2024/06/05 15:21

该题是一道经典的最小割题目 。  最近学习图论以来一直不是很明白最小割是什么,通过该题,有了一定的认识 。

首先我们要知道最大流最小割定理 :对于任意一个只有一个源和一个汇的图来说,从源到汇的最大流等于最小割 。

也就是说,对于一个图中的两个集合AB,通过删除一些边让他们恰好不连通,那么删掉的这些边就叫做他们的割了 。  那么最小割和网络流有什么关系呢? 为什么最小割定理是对的呢? 其实很简单,刚才说了,我们要删除一些边让他们无法连通  。  对应到最大流中就是说残量网络中不存在增广路,那么此时的流就是最大流,自然也是最小割(因为我们还可以继续删边,不过无论怎么删都不会再存在增广路,但是这个割就大了)。 

理解了这一点之后,问题的关键就是如何建立模型,将割边所需的花费对应到最大流的流量上来 。

该题不同于普通的最小割,因为洞和草可以互相转化,那么我们先将问题简化,加入石头和草不能相互转化,那么怎么求最小割? 别跟我说暴力啊, 我们用网络流来求,那么我们可以将源点与草地连接,洞与汇点连接,且他们的容量都为无穷大就可以了,这样,使得网络不再连通之时洞和草就被完全割断了! 那么我们加上可以相互转化的条件后,显然要将费用和流量相对应,那么该题就迎仍而解了。 所以这也是为什么要将那些不可更改的草与源点的容量改成无穷大的原因了。

紫书上介绍的Edmonds-Karp算法确实比较慢,大概要跑0.666s的样子,套用训练指南上的Dinic算法,0.023s 。 大家暂时无需搞懂原理,接口类似套上就行,当然有兴趣的还是搞懂为好。

细节参见代码:

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int INF = 1000000000;const int maxn = 50*50 + 10;int T,cnt,n,m,dd,ff,bb,t,k,vis[maxn],cur[maxn],d[maxn];char s[maxn][maxn];struct Edge {  int from, to, cap, flow;};bool operator < (const Edge& a, const Edge& b) {  return a.from < b.from || (a.from == b.from && a.to < b.to);}struct Dinic {  int n, m, s, t;  vector<Edge> edges;    // 边数的两倍  vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号  bool vis[maxn];        // BFS使用  int d[maxn];           // 从起点到i的距离  int cur[maxn];         // 当前弧指针void init(int n) {    for(int i = 0; i < n; i++) G[i].clear();    edges.clear();}void AddEdge(int from, int to, int cap) {    edges.push_back((Edge){from, to, cap, 0});    edges.push_back((Edge){to, from, 0, 0});    m = edges.size();    G[from].push_back(m-2);    G[to].push_back(m-1);}bool BFS() {    memset(vis, 0, sizeof(vis));    queue<int> Q;    Q.push(s);    vis[s] = 1;    d[s] = 0;    while(!Q.empty()) {      int x = Q.front(); Q.pop();      for(int i = 0; i < G[x].size(); i++) {        Edge& e = edges[G[x][i]];        if(!vis[e.to] && e.cap > e.flow) {          vis[e.to] = 1;          d[e.to] = d[x] + 1;          Q.push(e.to);        }      }    }    return vis[t];}int DFS(int x, int a) {    if(x == t || a == 0) return a;    int flow = 0, f;    for(int& i = cur[x]; i < G[x].size(); i++) {      Edge& e = edges[G[x][i]];      if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {        e.flow += f;        edges[G[x][i]^1].flow -= f;        flow += f;        a -= f;        if(a == 0) break;      }    }    return flow;}int Maxflow(int s, int t) {    this->s = s; this->t = t;    int flow = 0;    while(BFS()) {      memset(cur, 0, sizeof(cur));      flow += DFS(s, INF);    }    return flow;  }};Dinic g;int main() {    scanf("%d",&T);    while(T--) {        scanf("%d%d%d%d%d",&m,&n,&dd,&ff,&bb);        for(int i=1;i<=n;i++) scanf("%s",s[i]+1);        int ans = 0; g.init(n*m+5);        for(int i=1;i<=n;i++) {            if(s[i][1] == '.') { s[i][1] = '#'; ans += ff; }            if(s[i][m] == '.') { s[i][m] = '#'; ans += ff; }        }        for(int i=1;i<=m;i++) {            if(s[1][i] == '.') { s[1][i] = '#'; ans += ff; }            if(s[n][i] == '.') { s[n][i] = '#'; ans += ff; }        }        for(int i=1;i<=n;i++) {            for(int j=1;j<=m;j++) {                if(s[i][j] == '#') {                    int cap = INF;                    if(i != 1 && i != n && j != 1 && j != m) cap = dd;                    g.AddEdge(0,(i-1)*m+j,cap);                }                else g.AddEdge((i-1)*m+j,n*m+1,ff);                if(i > 1) g.AddEdge((i-1)*m+j,(i-1-1)*m+j,bb);                if(i < n) g.AddEdge((i-1)*m+j,(i)*m+j,bb);                if(j > 1) g.AddEdge((i-1)*m+j,(i-1)*m+j-1,bb);                if(j < m) g.AddEdge((i-1)*m+j,(i-1)*m+j+1,bb);            }        }        ans += g.Maxflow(0,n*m+1);        printf("%d\n",ans);    }    return 0;}


1 0
原创粉丝点击