圖論---網(wǎng)絡(luò)流

最大流

EdmondsKarp

bfs找路,途中記錄前驅(qū)節(jié)點(diǎn)
讓后從匯點(diǎn)遍歷到起點(diǎn),找到最小flow
再次遍歷,更新沿途邊
累加答案,繼續(xù)bfs

#define mem(x,y) memset(x,y,sizeof(x))
#define SIZE 1005
const int INF=0x3f3f3f3f;
int G[SIZE][SIZE],pre[SIZE];
bool vst[SIZE];
bool bfs(int s,int t){
    queue<int> que;
    mem(vst,0);
    mem(pre,-1);
    pre[s]=s;
    vst[s]=true;
    que.push(s);
    while (!que.empty()) {
        int u=que.front();que.pop();
        for(int i=s;i<=t;++i){//遍歷所有點(diǎn)
            if(G[u][i]&&!vst[i]){
                pre[i]=u;
                vst[i]=true;
                if(i==t)return true;
                que.push(i);
            }
        }
    }
    return false;
}
int EK(int s,int t){
    int ans=0;
    while (bfs(s,t)) {
        int minflow=INF;
        for(int i=t;i!=s;i=pre[i]){
            minflow=min(minflow,G[pre[i]][i]);
        }
        for(int i=t;i!=s;i=pre[i]){
            G[pre[i]][i]-=minflow;
            G[i][pre[i]]+=minflow;
        }
        ans+=minflow;
    }
    return ans;
}

dinic

多路增廣+當(dāng)前弧優(yōu)化
建圖時候建反向邊(前向星邊id從0開始,這樣edge[i^1]就是反邊)
首先bfs分層,維護(hù)每個點(diǎn)到匯點(diǎn)的距離(每個邊距離都看做1)
然后對分過層的圖dfs,每找到一條通路,沿途邊邊權(quán)減去流量,反邊加上流量,反復(fù)找直到?jīng)]有
再次bfs直到?jīng)]有
for(int &i=curedge[u];i!=-1;i=edge[i].nxt)
這句當(dāng)前弧優(yōu)化可能看不懂,代碼中有詳細(xì)注釋
head[] edge[] 是鏈?zhǔn)角跋蛐?/a>
curedge[] 當(dāng)前弧優(yōu)化使用

#include <queue>
#include <string.h>
#define mem(x,y) memset(x,y,sizeof(x))
#define SIZE 1000
struct node{
    int v,nxt,w;//指向那個點(diǎn) 下條邊id 權(quán)值
}edge[SIZE*2];
int head[SIZE],curedge[SIZE],dis[SIZE],ecnt,s,t;
inline void init(){
    ecnt=0;//從偶數(shù)開始都行
    mem(head,-1);
}
inline void addedge(int u,int v,int w){
    edge[ecnt].v=v;
    edge[ecnt].w=w;
    edge[ecnt].nxt=head[u];
    head[u]=ecnt;
    ecnt++;
    swap(u,v);//添加反邊
    edge[ecnt].v=v;
    edge[ecnt].w=0;
    edge[ecnt].nxt=head[u];
    head[u]=ecnt;
    ecnt++;
}
bool bfs(){
    mem(dis,-1);//不能少
    dis[t]=0;//s是起點(diǎn),t是終點(diǎn),分層
    queue<int> que;
    que.push(t);
    while(!que.empty()){
        int u=que.front();que.pop();
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            if(dis[edge[i].v]==-1&&edge[i^1].w>0){
                dis[edge[i].v]=dis[u]+1;
                que.push(edge[i].v);
            }
        }
    }
    return dis[s]!=-1;//沒有辦法從s到t返回false
}
int dfs(int u,int v,int flow){
    if(u==t)return flow;
    int delta=flow;//表示前面輸送過來的流量有多少被擋住了,初始化為所有
    for(int &i=curedge[u];i!=-1;i=edge[i].nxt){
        //當(dāng)前弧優(yōu)化,& 引用是重點(diǎn)
        if(dis[u]==dis[edge[i].v]+1&&edge[i].w>0){
            int d=dfs(edge[i].v,v,min(delta,edge[i].w));
            edge[i].w-=d;edge[i^1].w+=d;
            delta-=d;//可以放行d的流量,被擋住的流量變少了
            if(delta==0)break;//這句對當(dāng)前弧優(yōu)化很重要,這時進(jìn)來的流量全都放行了,那么當(dāng)前這條路可能剛好被填滿,也可能還有寬裕(如果delta!=0,說明這條路肯定占滿了)
            //因為可能有寬裕,所以這條路以后還要走,這時候break;curedge[u]=i,前面的路因為都滿了,所以直接舍去,下次到這個點(diǎn)直接從第i個邊開始,這就是當(dāng)前弧優(yōu)化。
        }
    }
    return flow-delta;//送進(jìn)來的 - 擋住的 = 有效流量
}
int dinic(){
    int ans=0;
    while (bfs()) {//分層(計算距離)
        for(int i=s;i<=t;i++)//每bfs一次,層次就刷新,所以也要重置當(dāng)前弧
            curedge[i]=head[i];
        ans+=dfs(s,t,INF);//從點(diǎn)1到點(diǎn)n最大流,輸入流量無窮大
    }
    return ans;
}

費(fèi)用流

spfa

求最小費(fèi)用最大流
建邊時候反邊cost是原來的負(fù)數(shù)
用spfa求出一條s到t的最短路,途中
pre[]數(shù)組記錄當(dāng)前點(diǎn)是從哪個邊過來的(放了一個邊id)
然后通過pre[]從t一直遍歷到s,找到途中最小流量Min
再遍歷一次,更新途中邊的容量,更新答案
再次spfa直到?jīng)]有通路

#include <queue>
#include <string.h>
#define mem(x,y) memset(x,y,sizeof(x))
#define SIZE 1000
struct node{
    int v,nxt,cap,cost;//指向那個點(diǎn) 下條邊id 流通容量 這條路花費(fèi)(看情況有時候是單價,記得改dfs里面的跟新)
}edge[40010];
int head[SIZE],dis[SIZE],pre[SIZE],ecnt,s,t;
bool vst[SIZE];
inline void init(){
    ecnt=0;//從偶數(shù)開始都行
    mem(head,-1);
}
inline void addedge(int u,int v,int cap,int cost){
    edge[ecnt].v=v;
    edge[ecnt].cap=cap;
    edge[ecnt].cost=cost;
    edge[ecnt].nxt=head[u];
    head[u]=ecnt;
    ecnt++;
    swap(u,v);//添加反邊
    edge[ecnt].v=v;
    edge[ecnt].cap=0;
    edge[ecnt].cost=-cost;//注意反邊花費(fèi)為負(fù)
    edge[ecnt].nxt=head[u];
    head[u]=ecnt;
    ecnt++;
}
bool spfa(){
    mem(dis,INF);//不能少
    mem(vst,0);
    mem(pre,-1);
    dis[s]=0;//s是起點(diǎn),t是終點(diǎn),分層
    vst[s]=true;
    queue<int> que;
    que.push(s);
    while(!que.empty()){
        int u=que.front();que.pop();
        vst[u]=false;
        for(int i=head[u];i!=-1;i=edge[i].nxt){
            if(dis[u]+edge[i].cost<dis[edge[i].v]&&edge[i].cap>0){
                //通過點(diǎn)u更短
                dis[edge[i].v]=dis[u]+edge[i].cost;
                pre[edge[i].v]=i;
                if(!vst[edge[i].v]){
                    vst[edge[i].v]=true;
                    que.push(edge[i].v);
                }
            }
        }
    }
    return pre[t]!=-1;//沒有辦法從s到t返回false
}
int mfmc(int & cost){//cost 按引用更新
    int flow=0; cost=0;
    while (spfa()) {
        int Min=INF;
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].v]){//i是邊的id,得到另一個頂點(diǎn)edge[i^1].v
            if(Min>edge[i].cap){
                Min=edge[i].cap;
            }
        }
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].v]){
            edge[i].cap-=Min;
            edge[i^1].cap+=Min;

        }
        cost+=dis[t]*Min;
        flow+=Min;
    }
    return flow;
}

相關(guān)閱讀更多精彩內(nèi)容

  • 現(xiàn)實生活中有很大一類問題可以用簡潔明了的圖論語言來描述,可以轉(zhuǎn)化為圖論問題。 相關(guān)定義 圖可以表示為G=(V, E...
    芥丶未央閱讀 1,834評論 0 7
  • 數(shù)據(jù)結(jié)構(gòu)學(xué)不好,c++就到后面會很迷,數(shù)據(jù)結(jié)構(gòu)真滴很重要啊,上機(jī)題一定要認(rèn)真做,緊密的和實際操作的代碼聯(lián)系在一起是...
    Nancy_Shi閱讀 954評論 0 4
  • 一些概念 數(shù)據(jù)結(jié)構(gòu)就是研究數(shù)據(jù)的邏輯結(jié)構(gòu)和物理結(jié)構(gòu)以及它們之間相互關(guān)系,并對這種結(jié)構(gòu)定義相應(yīng)的運(yùn)算,而且確保經(jīng)過這...
    Winterfell_Z閱讀 6,604評論 0 13
  • 這個圣誕節(jié)假期,我給孩子的告誡,同時也是對自己的告誡:你把時間花在了這里,就不可能花在那里。上帝最大的公平是,所有...
    摸魚哥閱讀 1,323評論 0 0
  • 資本通過投資其他企業(yè)獲得回報,我認(rèn)為最核心的能力是風(fēng)險定價能力。如果一個團(tuán)隊的人員構(gòu)成都是偏向某個方向、專...
    劉朔_北京閱讀 171評論 0 0

友情鏈接更多精彩內(nèi)容