草地排水 洛谷P2740 最大流入门题目

题意

在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水。这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间。因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没的烦恼(不用担心,雨水会流向附近的一条小溪)。作为一名一流的技师,农夫约翰已经在每条排水沟的一端安上了控制器,这样他可以控制流入排水沟的水流量。

农夫约翰知道每一条排水沟每分钟可以流过的水量,和排水系统的准确布局(起点为水潭而终点为小溪的一张网)。需要注意的是,有些时候从一处到另一处不只有一条排水沟。

根据这些信息,计算从水潭排水到小溪的最大流量。对于给出的每条排水沟,雨水只能沿着一个方向流动,注意可能会出现雨水环形流动的情形。

输入格式

第1行: 两个用空格分开的整数N (0 <= N <= 200) 和 M (2 <= M <= 200)。N是农夫John已经挖好的排水沟的数量,M是排水沟交叉点的数量。交点1是水潭,交点M是小溪。

第二行到第N+1行: 每行有三个整数,Si, Ei, 和 Ci。Si 和 Ei (1 <= Si, Ei <= M) 指明排水沟两端的交点,雨水从Si 流向Ei。Ci (0 <= Ci <= 10,000,000)是这条排水沟的最大容量。

输出格式

输出一个整数,即排水的最大流量。

//下面是样例输入
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
//样例输出
50

解题思路

这个是我学习最大流算法时,看到有博客说这个是个入门题目,确实很基础。

具体的最大流讲解就不说了,推荐看这个博客网络流入门之最大流算法(增广路算法 由Ford-Fulkerson到Edmonds-Karp到dinic) 包看懂,确实讲的不错,尤其是他的代码实现是那种最基本的形式。

下面的代码两个版本,一个是基于他的进行了更改,另一个是看的《算法竞赛入门经典(第二版)》368页的实现代码,更加的高级吧。

代码实现

//我的EK写法。。。建议邻接矩阵
//题目:洛谷p2740 草地排水
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int m,n,cap[205][205],pre[205],flow[205][205];
int vis[205];
bool bfs(int s,int t)
{
//    memset(pre,-1,sizeof(pre));
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s]=inf;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            //这里vis不仅代表标记,还记录了到这个结点可以减去的最小流量值
            //这样就省去了max_flow中的第一个for循环
            if(vis[i]==0 && i!=now && cap[now][i]-flow[now][i]>0)
            {
                q.push(i);
                pre[i]=now;
                vis[i]=min(vis[now], cap[now][i]-flow[now][i]);
                if(i==t)    return true;    //找到了
            }
        }
    }
    return false;
}
int max_flow(int s,int t)
{
    int ans=0;

    while(bfs(s,t))
    {
        //这个for循环的作用就是找到一条到达重点的路中,最小的剩余流量值
//      int delta=0x7fffffff;
//      for(int i=t;i!=s;i=pre[i])
//      {
//          delta=min(delta,cap[pre[i]][i]-flow[pre[i]][i]);
//      }
        for(int i=t;i!=s;i=pre[i])
        {
//          flow[pre[i]][i]+=delta;
//          flow[i][pre[i]]-=delta;
            flow[pre[i]][i]+=vis[t];
            flow[i][pre[i]]-=vis[t];
        }
        ans+=vis[t];    //这是一个结论 记住就行
    }
    return ans;
}
int main()
{
    cin>>m>>n;
    for(int i=1;i<=m;i++)
    {
        int x,y,rl;
        cin>>x>>y>>rl;
        //必须是+= 超级阴人.....没有看到一处到另一处不止一条排水沟
        cap[x][y]+=rl;
        cap[y][x]+=rl;
        flow[x][y]=0;
        flow[y][x]+=rl;
    }
    cout<<max_flow(1,n);
    return 0;
}
//下面的代码是自己对《算法竞赛入门经典》中关于最大流的写法
//因为这个题中,两个点之间不仅只有一条路,所以开始使用邻接矩阵,然后再使用加边函数来进行操作
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=300;
struct Edge{
    int from, to, cap, flow;
    Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f){}
};
vector<Edge> edges;
vector<int> G[maxn];//邻接表,G[i][j],表示结点i的第j条边在edges[j]中。
int vis[maxn], pre[maxn], cap[205][205];//pre用来记录路径
int n, m;
void init()
{
    for(int i=0; i<=n; i++) G[i].clear();
    edges.clear();
}
inline void addedge(int from, int to, int cap) //这里使用了inline函数,一定条件下可以减少运行时间
{
    edges.push_back(Edge(from, to, cap, 0));
    edges.push_back(Edge(to, from, cap, cap));
    int m=edges.size();
    G[from].push_back(m-2);
    G[to].push_back(m-1);
}
bool bfs(int s, int t)
{
    memset(vis, 0,sizeof(vis));
    queue<int> Q;
    Q.push(s);
    vis[s]=inf;
    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)
            {
                pre[e.to]=G[x][i];
                vis[e.to]=min(vis[x], e.cap-e.flow);
                Q.push(e.to);
            }
        }
        if(vis[t])  return true;
    }
    return false;
}
int maxflow(int s, int t)
{
    int flow = 0;
    while(bfs(s, t))
    {
        for(int u=t; u!=s; u=edges[pre[u]].from)
        {
            edges[pre[u]].flow += vis[t];
            edges[pre[u]^1].flow -= vis[t];
        }
        flow += vis[t];
    }
    return flow;
}
int main()
{
    int x, y, rl;
    scanf("%d%d", &m, &n);
    memset(cap, 0, sizeof(cap));
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d%d", &x, &y, &rl);
        cap[x][y]+=rl;
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(cap[i][j]!=0)
                addedge(i, j, cap[i][j]);
        }
    }
    printf("%d\n", maxflow(1, n));
    return 0;
 } 
12-26 08:53