網絡流問題

一、最大流

我們有一張圖,要求從源點流向匯點的最大流量(可以有很多條路到達匯點),就是我們的最大流問題。EK算法模板如下

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x7ffffff;

queue <int> q;
int n,m,x,y,s,t,g[201][201],pre[201],flow[201],maxflow; 
//g鄰接矩陣存圖,pre增廣路徑中每個點的前驅,flow源點到這個點的流量 

inline int bfs(int s,int t)
{
    while (!q.empty()) q.pop();
    for (int i=1; i<=n; i++) pre[i]=-1;
    pre[s]=0;
    q.push(s);
    flow[s]=INF;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        if (x==t) break;
        for (int i=1; i<=n; i++)
          //EK一次只找一個增廣路 
          if (g[x][i]>0 && pre[i]==-1)
          {
            pre[i]=x;
            flow[i]=min(flow[x],g[x][i]);
            q.push(i);
          }
    }
    if (pre[t]==-1) return -1;
    else return flow[t];
}

//increase爲增廣的流量 
void EK(int s,int t)
{
    int increase=0;
    while ((increase=bfs(s,t))!=-1)//這裏的括號加錯了!Tle 
    {//迭代 
        int k=t;
        while (k!=s)
        {
            int last=pre[k];//從後往前找路徑
            g[last][k]-=increase;
            g[k][last]+=increase;
            k=last;
        }
        maxflow+=increase;
    }
}

int main()
{
    scanf("%d%d%d%d",&n, &m, &s, &t);
    for (int i=1; i<=m; i++)
    {
        int z;
        scanf("%d%d%d",&x,&y,&z);
        g[x][y]+=z;//此處不可直接輸入,要+= 
    }
    EK(s,t);
    printf("%d",maxflow);
    return 0;
}

速度更快的Dinic算法模板如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int inf=1e9;

int n,m,x,y,z,s,t,maxflow,deep[500];//deep深度 
struct Edge{
    int next,to,dis;
}edge[500];
int num_edge=-1,head[500],cur[500];//cur用於複製head 
queue <int> q;

void add_edge(int from,int to,int dis,bool flag)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    if (flag) edge[num_edge].dis=dis;//反圖的邊權爲 0
    head[from]=num_edge;
}

//bfs用來分層 
bool bfs(int s,int t)
{
    memset(deep,0x7f,sizeof(deep));
    while (!q.empty()) q.pop();
    for (int i=1; i<=n; i++) cur[i]=head[i];
    deep[s]=0;
    q.push(s);

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i!=-1; i=edge[i].next)
        {
            if (deep[edge[i].to]>inf && edge[i].dis)//dis在此處用來做標記 是正圖還是返圖 
            {
                deep[edge[i].to]=deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
    if (deep[t]<inf) return true;
    else return false;
}

//dfs找增加的流的量 
int dfs(int now,int t,int limit)//limit爲源點到這個點的路徑上的最小邊權 
{
    if (!limit || now==t) return limit;

    int flow=0,f;
    for (int i=cur[now]; i!=-1; i=edge[i].next)
    {
        cur[now]=i;
        if (deep[edge[i].to]==deep[now]+1 && (f=dfs(edge[i].to,t,min(limit,edge[i].dis))))
        {
            flow+=f;
            limit-=f;
            edge[i].dis-=f;
            edge[i^1].dis+=f;
            if (!limit) break;
        }
    }
    return flow;
}

void Dinic(int s,int t)
{
    while (bfs(s,t))
        maxflow+=dfs(s,t,inf);
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1; i<=m; i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z,1); add_edge(y,x,z,0);
    }
    Dinic(s,t);
    printf("%d",maxflow);
    return 0;
}

二、最小費用最大流

假如我們有一個流量網絡,現在每個邊除了流量,現在還有一個單位費用,這條邊的費用相當於它的單位費用乘上它的流量,我們要保持最大流的同時,還要保持邊權最小,這就是最小費用最大流問題。 因爲在一個網絡流圖中,最大流量只有一個,但是“流法”有很多種,每種不同的流法所經過的邊不同因此費用也就不同,所以需要用到最短路算法。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100010;

bool vis[maxn];
int n,m,s,t,x,y,z,f,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost;
//dis最小花費;pre每個點的前驅;last每個點的所連的前一條邊;flow源點到此處的流量 
struct Edge{
    int to,next,flow,dis;//flow流量 dis花費 
}edge[maxn];
int head[maxn],num_edge; 
queue <int> q;

void add_edge(int from,int to,int flow,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].flow=flow;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;

    while (!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for (int i=head[now]; i!=-1; i=edge[i].next)
        {
            if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正邊 
            {
                dis[edge[i].to]=dis[now]+edge[i].dis;
                pre[edge[i].to]=now;
                last[edge[i].to]=i;
                flow[edge[i].to]=min(flow[now],edge[i].flow);//
                if (!vis[edge[i].to])
                {
                    vis[edge[i].to]=1;
                    q.push(edge[i].to);
                }
            }
        }
    }
    return pre[t]!=-1;
}

void MCMF()
{
    while (spfa(s,t))
    {
        int now=t;
        maxflow+=flow[t];
        mincost+=flow[t]*dis[t];
        while (now!=s)
        {//從源點一直回溯到匯點 
            edge[last[now]].flow-=flow[t];//flow和dis容易搞混 
            edge[last[now]^1].flow+=flow[t];
            now=pre[now];
        }
    }
}

int main()
{
    memset(head,-1,sizeof(head)); num_edge=-1;//初始化 
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1; i<=m; i++)
    {
        scanf("%d%d%d%d",&x,&y,&z,&f);
        add_edge(x,y,z,f); add_edge(y,x,0,-f);
    }
    MCMF();
    printf("%d %d",maxflow,mincost);
    return 0;
}

三、最小割

割其實就是刪邊的意思,當然最小割就是割掉X條邊來讓S跟T不互通。我們要求X條邊加起來的流量綜合最小。這就是最小割問題。最小割等於最大流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章