POJ1273 Drainage Ditches

Drainage Ditches

Time Limit: 1000MS Memory Limit: 10000KB

Description

Every time it rains on Farmer John’s fields, a pond forms over Bessie’s favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie’s clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch.
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network.
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle。

Input

The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

Output

For each case, output a single integer, the maximum rate at which water may emptied from the pond.

Sample Input

5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10

Sample Output

50

解题

题意分析

抛开故事情节,其实就是一个明显的网络流问题。

解法一:Edmonds-Karp算法

概念

求最大流的过程,就是不断找到一条源到汇的路径,然后构建残余网络,再在残余网络上寻找新的路径,使总流量增加,然后形成新的残余网络,再寻找新路径…..直到某个残余网络上找不到从源到汇的路径为止,最大流就算出来了。每次寻找新流量并构造新残余网络的过程, 就叫做寻找流量的“增广路径”,也叫“增广“。
在每次增广的时候,选择从源到汇的具有最少边数的增广路径,即不是通过dfs寻找增广路径,而是通过bfs寻找增广路径。这就是Edmonds-Karp 最短增广路算法。可以证明这种算法的复杂度上限为O(n*m^2)(n是
点数,m是边数)(事实上,楼猪并不会证)

代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int n,m;
int fa[205];//记录父节点
bool vis[205];//记录是否已访问
const int INF=10000005;
int maxflow[205][205];//存储可用流量
unsigned int find_maxflow()
{
    queue<int> q;
    memset(fa,0,sizeof(fa));
    memset(vis,0,sizeof(vis));
    bool find_path=false;
    fa[1]=0,vis[1]=1;
    int x;
    q.push(1);
    while(!q.empty()&&!find_path)//队列空了或找到源汇路就退出
    {
        x=q.front();
        q.pop();
        for(int i=1;i<=m;i++)
        {
            if(maxflow[x][i]>0&&!vis[i])//还有可用流量且还未被访问
            {
                fa[i]=x;
                vis[i]=1;
                if(i==m)//找到源汇路
                {
                    find_path=true ;
                    break;
                }
                else
                    q.push(i);
            }
        }
    }
    if(!find_path)
        return 0;
    int minflow=INF;
    x=m;
    //寻找源到汇路径上容量最小的边,其容量就是此次增加的总流量  
    while(fa[x])
    {
        minflow=min(minflow,maxflow[fa[x]][x]);
        x=fa[x];
    }
    x=m;
    //沿此路径添加反向边,同时修改路径上每条边的容量
    while(fa[x])
    {
        maxflow[fa[x]][x]-=minflow;
        maxflow[x][fa[x]]+=minflow;
        x=fa[x];
    }
    return minflow;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        memset(maxflow,0,sizeof(maxflow));
        for(int i=1;i<=n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            maxflow[u][v]+=w;
        }
        unsigned int Maxflow=0,Flow;
        while(Flow=find_maxflow())
            Maxflow+=Flow;
        printf("%d\n",Maxflow);
}}

解法二 Dinic算法

概念

先利用BFS对残余网络分层(一个节点的层数,就是源点到它最少要经过的边数)。 分完层后,利用DFS从前一层向后一层反复寻找增广路。 ((即要求DFS的每一步都必须要走到下一层的节点)。 DFS过程中,要是碰到了汇点,则说明找到了一条增广 路径。此时要增加总流量的值,消减路径上各边的容量,并添加反向边,即所谓的进行增广。
DFS找到一条增广路径后,并不立即结束,而是回溯后
继续DFS寻找下一个增广路径。
回溯到哪个节点呢?
回溯到的节点u满足以下条件:
1) DFS搜索树的树边(u,v)上的容量已经变成0 。即刚刚找
到的增广路径上所增加的流量,等于(u,v)本次增广前的容量。(DFS的过程中,是从u走到更下层的v 的)。
2)u是满足条件 1)的最上层的节点
如果回溯到源点而且无法继续往下走了,DFS结束。
因此,一次DFS过程中,可以找到多条增广路径。
DFS结束后,对残余网络再次进行分层,然后再进行DFS。
当残余网络的分层操作无法算出汇点的层次(即BFS到达不了汇点)时,算法结束,最大流求出。
时间复杂度为O(m*n^2)(n是点数,m是边数),是比Edmonds-Karp算法代码量更大但更快的方法,推荐在算法竞赛中选择Dinic。

代码

事实上,这代码并不是楼主自己写的。

#include <iostream>
#include <queue>   
#include <vector>  
#include <algorithm>   
#include <deque>   
using namespace std;  
#define INFINITE 999999999
int G[300][300];           
bool Visited[300];  
int Layer[300];     int n,m; //1是源点,m是汇点  
bool CountLayer()   {  
             int layer = 0;     deque<int>q;  
             memset(Layer,0xff,sizeof(Layer)); //都初始化成-1  
             Layer[1] = 0;             q.push_back(1);  
             while( ! q.empty()) {  
                          int v = q.front();  
                          q.pop_front();  
                          for( int j = 1; j <= m; j ++ ) {  
                                       if( G[v][j] > 0 && Layer[j] == -1 ) {  
                                       //Layer[j] == -1 说明j还没有访问过  
                                                    Layer[j] = Layer[v] + 1;   
                                                    if( j == m )  //分层到汇点即可  
                                                                return true;  
                                                   else   
                                                                q.push_back(j);  
                                       }  
                          }  
             }
             return false;  
} 
int Dinic()  
{  
            int i;       int s;  
            int nMaxFlow = 0;  
            deque<int> q; //DFS用的栈  
            while( CountLayer() ) { // 只要能分层  
                         q.push_back(1);         //源点入栈                     
                         memset(Visited,0,sizeof(Visited));    Visited[1] = 1;  
                         while( !q.empty()) {  
                                     int nd = q.back();  
                                     if( nd == m ) { // nd是汇点  
                                                 //在栈中找容量最小边  
                                                  int nMinC = INFINITE;  
                                                  int nMinC_vs; //容量最小边的起点  
                                                 for( i = 1;i < q.size(); i ++ ) {  
                                                              int vs = q[i-1];  
                                                              int ve = q[i];  
                                                              if( G[vs][ve] > 0 ) {  
                                                                          if( nMinC > G[vs][ve] ) {  
                                                                                       nMinC = G[vs][ve];  
                                                                                       nMinC_vs = vs;  
                                                                          }  
                                                              }  
                                                 }  
 //增广,改图  
             nMaxFlow += nMinC;  
             for( i = 1;i < q.size(); i ++ ) {  
                          int vs = q[i-1];  
                          int ve = q[i];  
                          G[vs][ve] -= nMinC; //修改边容量   
                          G[ve][vs] += nMinC; //添加反向边  
             }  
             //退栈到 nMinC_vs成为栈顶,以便继续dfs  
             while( !q.empty() && q.back() != nMinC_vs ) {   
                         Visited[q.back()] = 0;   
                          q.pop_back();  
             }  

      }  
      else { //nd不是汇点  
             for( i = 1;i <= m; i ++ )  {  
                   if( G[nd][i] > 0 && Layer[i] == Layer[nd] + 1 &&  
                          ! Visited[i]) {  
                                      // 只往下一层的没有走过的节点走  
                                      Visited[i] = 1;  
                                      q.push_back(i);  
                                      break;  
                          }  

if( i > m)  //找不到下一个点  
                                                                    q.pop_back(); // 回溯  
                                         }  
                           }  
              }  
              return nMaxFlow;  
}  
int main()  
{  
              while (cin >> n >> m ) {  
                           int i,j,k;  
                           int s,e,c;  
                           memset( G,0,sizeof(G));  
                           for( i = 0;i < n;i ++ ) {  
                                         cin >> s >> e >> c;  
                                         G[s][e] += c; //两点之间可能有多条边  
                           }  
                           cout << Dinic() << endl;  

              }  
              return 0;  
}  
若有不足之处,望多包涵
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
旅游社交小程序功能有管理员和用户。管理员有个人中心,用户管理,每日签到管理,景点推荐管理,景点分类管理,防疫查询管理,美食推荐管理,酒店推荐管理,周边推荐管理,分享圈管理,我的收藏管理,系统管理。用户可以在微信小程序上注册登录,进行每日签到,防疫查询,可以在分享圈里面进行分享自己想要分享的内容,查看和收藏景点以及美食的推荐等操作。因而具有一定的实用性。 本站后台采用Java的SSM框架进行后台管理开发,可以在浏览器上登录进行后台数据方面的管理,MySQL作为本地数据库,微信小程序用到了微信开发者工具,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得旅游社交小程序管理工作系统化、规范化。 管理员可以管理用户信息,可以对用户信息添加修改删除。管理员可以对景点推荐信息进行添加修改删除操作。管理员可以对分享圈信息进行添加,修改,删除操作。管理员可以对美食推荐信息进行添加,修改,删除操作。管理员可以对酒店推荐信息进行添加,修改,删除操作。管理员可以对周边推荐信息进行添加,修改,删除操作。 小程序用户是需要注册才可以进行登录的,登录后在首页可以查看相关信息,并且下面导航可以点击到其他功能模块。在小程序里点击我的,会出现关于我的界面,在这里可以修改个人信息,以及可以点击其他功能模块。用户想要把一些信息分享到分享圈的时候,可以点击新增,然后输入自己想要分享的信息就可以进行分享圈的操作。用户可以在景点推荐里面进行收藏和评论等操作。用户可以在美食推荐模块搜索和查看美食推荐的相关信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值