天天看點

線性規劃網絡流之最短增廣路算法

線性規劃網絡流之最短增廣路算法

代碼實作

/*
最短增廣路C++實作
參考資料:《趣學算法》陳小玉 人民郵電出版社
*/
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int MAXNODESIZE=100;//網絡最大節點數
const int INF=(1<<30)-1;
int residualNetwork[MAXNODESIZE][MAXNODESIZE];//殘餘網絡
int realFlowNetwork[MAXNODESIZE][MAXNODESIZE];//實流網絡
int preArray[MAXNODESIZE];//前驅數組
bool looked[MAXNODESIZE];//通路數組:記錄節點是否已經通路過了
int nodeSize,edgeSize;//節點個數與邊個數

bool bfs(int snode,int enode);//bfs找可增廣路
int EK(int snode,int enode);
//找最短增廣路具體算法:對實流網絡以及殘餘網絡的操作
void print();//輸出實流網絡

int main(void){
    int u,v,w;
    //殘餘網絡初始化為0
    memset(residualNetwork,0,sizeof(residualNetwork));
    //實流網絡初始化為0
    memset(realFlowNetwork,0,sizeof(realFlowNetwork));
    cout<<"請輸入結點個數n與邊的數量m: \n";
    cin>>nodeSize>>edgeSize;
    cout<<"請輸入兩個結點u,v以及u--v的最大容量cap:\n";
    for(int i=1;i<=edgeSize;i++){
        cin>>u>>v>>w;
        residualNetwork[u][v]+=w;
    }
    cout<<"網絡的最大流值為: "<<EK(1,nodeSize)<<endl;
    print();//輸出實流網絡
    return 0;
}
//bfs找可增廣路
bool bfs(int snode,int enode){
    //初始化前驅數組
    memset(preArray,-1,sizeof(preArray));
    //初始化通路數組
    memset(looked,false,sizeof(looked));
    queue<int>Queue;
    //源點設為已通路
    looked[snode]=true;
    //源點入隊列
    Queue.push(snode);
    while(!Queue.empty()){//如果隊列不為空則bfs
        int now=Queue.front();
        Queue.pop();
        for(int i=1;i<=nodeSize;i++){//尋找可增廣路
            if(!looked[i]&&residualNetwork[now][i]>0){//now-->i:i沒有通路過,且殘餘網絡now-->i還可以增廣
                looked[i]=true;//結點i設為已通路
                preArray[i]=now;//記錄結點i的前驅結點
                if(i==enode){//到達彙點,找到一條可增廣路
                    return true;
                }
                Queue.push(i);//結點i入隊列
            }
        }
    }
    return false;//找不到可增廣路
}
//找最短增廣路具體算法:對實流網絡以及殘餘網絡的操作
int EK(int snode,int enode){
    int v,w,d,maxflow;//v--->w  d:可增廣路的可增廣量  maxflow:最大流量
    maxflow=0;//初始化最大流值
    while(bfs(snode,enode)){//不斷更新網絡,以達到不再可增廣
        v=enode;
        d=INF;
        while(v!=snode){//尋找可增廣路的可增量:在可增廣路中找最小的邊權值
            w=preArray[v];
            if(d>residualNetwork[w][v]){
                d=residualNetwork[w][v];
            }
            v=w;
        }
        maxflow+=d;//将新加的量加到總流量上
        //更新網絡
        v=enode;
        while(v!=snode){
            w=preArray[v];
            residualNetwork[w][v]-=d;//殘餘網絡更新
            residualNetwork[v][w]+=d;
            //實流網絡更新
            if(realFlowNetwork[v][w]>0){//正常來說是w--->v增廣,如果[v][w]>0則代表實際流向為w<---v,增廣量為w--->v的增加
                realFlowNetwork[v][w]-=d;
            }else{
                realFlowNetwork[w][v]+=d;
            }
            v=w;
        }
    }
    return maxflow;//傳回網絡最大流量
}
//輸出實流網絡
void print(){
    cout<<"實流網絡:\n";
    for(int i=1;i<=nodeSize;i++){
        cout<<"       v"<<i;
    }
    cout<<"\n";
    for(int i=1;i<=nodeSize;i++){
        cout<<"v"<<i;
        for(int j=1;j<=nodeSize;j++){
            cout<<"      "<<realFlowNetwork[i][j];
        }
        cout<<"\n";
    }
}
           

測試樣例

請輸入結點個數n與邊的數量m:
6 9
請輸入兩個結點u,v以及u--v的最大容量cap:
1 2 12
1 3 10
2 4 8
3 2 2
3 5 13
4 3 5
4 6 18
5 4 6
5 6 4
網絡的最大流值為: 18
實流網絡:
       v1       v2       v3       v4       v5       v6
v1      0      8      10      0      0      0
v2      0      0      0      8      0      0
v3      0      0      0      0      10      0
v4      0      0      0      0      0      14
v5      0      0      0      6      0      4
v6      0      0      0      0      0      0