1. 程式人生 > >最大流問題的幾種模板和思路

最大流問題的幾種模板和思路

核心思路:

(首先感謝網路大牛程式碼 附大牛網址: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對圖進行分層 之後再搜尋一下,會更省時間(下篇再見)