最大流问题的几种模板和思路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40924940/article/details/83479256

核心思路:

(首先感谢网络大牛代码 附大牛网址:https://blog.csdn.net/huzhengnan/article/details/7766446) 

我们首先 采用搜索的方式 来找到一条从 s-t 的路径 并且储存记录剩余量,之后采用链表的方式记录下我们走的每一条路,反向回去更新路的流量(正是有了反向流量 我们在搜素的时候 才能够有一个"反悔"的机会)最终,找到重点,记录最大流。

#include<iostream>
#include<queue>
using namespace std;
const int N=201;
const int INF=99999999;
int n,m,sum;//s,t为始点和终点
int flow[N][N],cap[N][N],a[N],pre[N];
//分别为:flow[u][v]为<u,v>流量、cap[u][v]为<u,v>容量、a[i]表示源点s到节点i的路径上的残留量、pre[i]记录i的前驱
void Edmonds_Karp(int s)
{
queue<int>q;//队列,用bfs找增广路
while(1)
{
   memset(a,0,sizeof(a));//每找一次,初始化一次
   a[s]=INF;
   q.push(s);//源点入队
   int sum=0;
   while(!q.empty())
   {
    int u=q.front();
    q.pop();
    for(int v=1;v<=m;v++)
    {
     if(!a[v]&&flow[u][v]<cap[u][v])
     {
      p[v]=u;
      q.push(v);
      a[v]=min(a[u],cap[u][v]-flow[u][v]);//s-v路径上的最小残量
     }
    }
   }
   if(a[m]==0)//找不到增广路,则当前流已经是最大流
    break;
   sum+=a[m];//流加上
   for(i=m;i!=s;i=p[i])// //从汇点顺着这条增广路往回走
   {
    flow[p[i]][i]+=a[m];//更新正向流量
    flow[i][p[i]]-=a[m];//更新反向流量
   }
}
printf("%d\n",sum);
}
int main()
{
    int v,u,w;
    while(scanf("%d%d",&n,&m)!=EOF)
   {
   memset(flow,0,sizeof(flow));//初始化
   memset(cap,0,sizeof(cap));
   while(n--)
   {
    scanf("%d%d%d",&u,&v,&w);
    cap[u][v]=w;
   }
   Edmonds_Karp(1);
   }
return 0;
}

目前我们很愉快的解决了一个基础最大流问题,但是我们如果更加追求一下完美,让整个程序占得空间更加小一些,用的数组更加小一些,是不是会更加优秀?

那么我们的思路可以放在流量和容量这个问题上了,如果流量超过容量是不可以的,那么我们流了多少,容量就相应的减去多少,是不是就会比我们增加“流量”这个数组里的值要来的更加方便?

#include<iostream>
#include<queue>
using namespace std;
const int N=201;
const int INF=99999999;
int n,m,sum,w;//s,t为始点和终点
int cap[N][N],a[N],p[N];
void Edmonds_Karp(int s)
{
  int i,u,v;
  queue<int>q;//队列,用bfs找增广路
  while(1)
 {
   memset(a,0,sizeof(a));//每找一次,初始化一次
   a[s]=INF;
   q.push(s);//源点入队
   while(!q.empty())
   {
    u=q.front();
    q.pop();
    for(v=1;v<=m;v++)
    {
     if(!a[v]&&cap[u][v]>0)
     {
      p[v]=u;
      q.push(v);
      a[v]=min(a[u],cap[u][v]);//s-v路径上的最小残量
     }
    }
   }
   if(a[m]==0)//找不到增广路,则当前流已经是最大流
    break;
   sum+=a[m];//流加上
   for(i=m;i!=s;i=p[i])// //从汇点顺着这条增广路往回走
   {
    cap[p[i]][i]-=a[m];//更新正向流量
    cap[i][p[i]]+=a[m];//更新反向流量
   }
 }
printf("%d\n",sum);
}
int main()
{

   int v,u;
   while(scanf("%d%d",&n,&m)!=EOF)
   {
       sum=0;//记录最大流量
       memset(cap,0,sizeof(cap));//初始化
       while(n--)
       {
        scanf("%d%d%d",&u,&v,&w);
        cap[u][v]=w;
       }
       Edmonds_Karp(1);
   }
return 0;
}

 那么就目前而看 基础的最大流算法我们已经搞得比较通透了。。但是这串代码只是个模板,还是要熟悉的运用。

总结: 最大流(用搜索实现,依靠的还有一些图论的思想,通过有向图反向地边权值来实现一种“反悔”的方法)

当然这还不是最优秀的算法。还可以先bfs对图进行分层 之后再搜索一下,会更省时间(下篇再见)

猜你喜欢

转载自blog.csdn.net/qq_40924940/article/details/83479256