网络流之最大流(关于poj1273)

//poj1273,网络流之最大流主要概念:
//1,源点,即整个图遍历的起点,POJ1273中是1,注意它只流出,不流入
//2.汇点,即终点POJ1273中为读入的下标,注意它只流入不流出。
//3.容量,即从一点到另一点的最大流量,上限值
//4.最大流问题就是知道源点,汇点,每一条边的容量,求从源点到汇点的最大流量
//5.残余网络:针对每条边的流量,就是每一条边还剩余的流量,正常是可以用另一个数组表示用了多少,这里为了节省空间,就将增加的流量减去,比如原先通过100,增加了10,那么还可以增加90
//6.增广路:简单来说就是一条通路,只不过每一条边(注意是这条路径上的!)的流量都比原来的残余网络(原来路径上的)要大。
//这里有几个问题值得关注:
//1,最大流的大小在哪取,简单来说就是从源点流出的总量,或流入汇点的总量,当然这里程序是记录下每一次增加的流量,因为初始状态没有流量,所以每增加一次,必然就多一些流量(因为增广路肯定经过源点的嘛)
//2. 何时取到最大流的图,即不存在增广路,因为如果存在增广路必然还可以增加流量。
//3.注意理解反向边,大致就是假设A路,B路都经过S边,如果我A路走S这条路就将另一种可能堵死了,所以A路要有反向边,B路可以通过反向边行进(因为经过一次反向边相当于把流量又还回去)
//这部分听起来很复杂,但是实现起来很容易,只需记录下该节点前一个是什么,然后找到增广路后倒退,(非常像并查集有木有~~)从前一个节点到后一个节点的通路可用流量减少,反向回路流量增加~~
//流程就是先用邻接矩阵存储,然后每一次找增广路,找的过程中事情较多,要记录下该节点的前一个,返回可增加的流量,寻找可增加流量的过程需要理解,可比作数组寻找最小值,即默认之前一个数组里保存的是可增加的最小值
//然后看后面路径是否小于最小值,如果后面可增长的路径已经小于最小值,那么可增长的最小值要更新,
//若增长路不存在,那么说明已经是最大流的图了,返回最大流,否则开始从尾向头更改路径剩余流量。
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int a[201][201];
int end;//终点
int path[201];
int bfs(int s,int e)
{
    int i,u,temp;
    int flow[201];
    int vis[201];
    memset(vis,0,sizeof(vis));
    memset(flow,0,sizeof(flow));
    queue<int>q;
    flow[s]=99999;//让起点的流量为最大值,因为接下来是比较从起点到周围的距离,必然是要记录从起点到该点的距离,所以flow[s]要最大
    vis[s]=1;//标记起点已经被访问
    q.push(s);
    while(!q.empty())
    {
     temp=q.front();
     q.pop();
     for (i=1;i<=end;i++)
        if ((!vis[i]&&a[temp][i]>0))//如果没有被访问过,同时存在回路
     {
         flow[i]=a[temp][i]<flow[temp]?a[temp][i]:flow[temp];//这就有点像在数组里面找最小值一样,从起点到终点存在回路,并且每一段都存在可增长值,最后找出所有可增值得最小值
         vis[i]=1;
         path[i]=temp;//记录路径((i的前一个是temo),因为马上还要修改这条路上的剩余流量
         q.push(i);
     }
   }
    return flow[e];//如果不存在增广路或者图已经断开,那么返回0,否则返回flow[e],即这次扫描图后得到的可增加的最小值
}
int EK(int s,int e)
{

    int max1=0,v,u;
    while(v=bfs(s,e))//当流量还可以增加时
    {
     u=e;//记录下终点,由终点向起点回归,此时不能改变e
      while(u!=s)
      {
          a[path[u]][u]-=v;//从前一个节点到后一个节点的可用流量要减少(相当于实际流通容量增加)
          a[u][path[u]]+=v;//同时增加反向回路,反向回路的值增加
          u=path[u];//同时往前递归,这让我想起了并查集的内容~~
      }
    max1+=v;//同时从起点流出的流量加v
    }
    return max1;
}
int main()
{
 int i,j,n,v,x,y;
 while(scanf("%d%d",&n,&end)==2)
 {
    memset(a,0,sizeof(a));
     for (i=1;i<=n;i++)
     {

         scanf("%d%d%d",&x,&y,&v);//从x到y的距离是v
         a[x][y]+=v;//这里很坑,可能出现相同的边
     }
     cout<<EK(1,end)<<endl;
 }
 return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值