网络流

网络流

最大流最小割

Ford-Fulkerson算法

用dfs实现的FF算法时间复杂度上界是 O ( m f ) O(mf) O(mf) ,其中 m m m为边数, f f f为最大流:

int n, m, s, t; // s是源点,t是汇点
bool vis[MAXN];
int dfs(int p = s, int flow = INF)//每次找一条增广路径
{
    if (p == t)
        return flow; // 到达终点,返回这条增广路的流量
    vis[p] = true;
    for (int eg = head[p]; eg; eg = edges[eg].next)
    {
        int to = edges[eg].to, vol = edges[eg].w, c;
        // 返回的条件是残余容量大于0、未访问过该点且接下来可以达到终点(递归地实现)
        // 传递下去的流量是边的容量与当前流量中的较小值
        if (vol > 0 && !vis[to] && (c = dfs(to, min(vol, flow))) != -1)
        {
            edges[eg].w -= c;
            edges[eg ^ 1].w += c;
            // 这是链式前向星取反向边的一种简易的方法
            // 建图时要把cnt置为1,且要保证反向边紧接着正向边建立
            return c;
        }
    }
    return -1; // 无法到达终点
}
inline int FF()
{
    int ans = 0, c;
    while ((c = dfs()) != -1)
    {
        memset(vis, 0, sizeof(vis));
        ans += c;
    }
    return ans;
}

Edmond-Karp算法

可以算是bfs实现的FF算法,复杂度上限是 O ( n m 2 ) O(nm^2) O(nm2)

int n, m, s, t, last[MAXN], flow[MAXN];
inline int bfs()
{
    memset(last, -1, sizeof(last));
    queue<int> q;
    q.push(s);
    flow[s] = INF;
    while (!q.empty())
    {
        int p = q.front();
        q.pop();
        if (p == t) // 到达汇点,结束搜索
            break;
        for (int eg = head[p]; eg; eg = edges[eg].next)
        {
            int to = edges[eg].to, vol = edges[eg].w;
            if (vol > 0 && last[to] == -1) // 如果残余容量大于0且未访问过(所以last保持在-1)
            {
                last[to] = eg;
                flow[to] = min(flow[p], vol);
                q.push(to);
            }
        }
    }
    return last[t] != -1;
}
inline int EK()
{
    int maxflow = 0;
    while (bfs())
    {
        maxflow += flow[t];
        for (int i = t; i != s; i = edges[last[i] ^ 1].to) // 从汇点原路返回更新残余容量
        {
            edges[last[i]].w -= flow[t];
            edges[last[i] ^ 1].w += flow[t];
        }
    }
    return maxflow;
}

Dinic算法

先用BFS分层,再用DFS寻找。它的时间复杂度上界是 O ( m n 2 ) O(mn^2) O(mn2)

int n, m, s, t, lv[MAXN], cur[MAXN]; // lv是每个点的层数(距源点的距离),cur用于当前弧优化标记增广起点
inline bool bfs() // BFS分层
{
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head)); // 当前弧优化初始化
    queue<int> q;
    q.push(s);
    while (!q.empty())
    {
        int p = q.front();
        q.pop();
        for (int eg = head[p]; eg; eg = edges[eg].next)
        {
            int to = edges[eg].to, vol = edges[eg].w;
            if (vol > 0 && lv[to] == -1)
                lv[to] = lv[p] + 1, q.push(to);
        }
    }
    return lv[t] != -1; // 如果汇点未访问过说明已经无法达到汇点,此时返回false
}
int dfs(int p = s, int flow = INF)
{
    if (p == t)
        return flow;
    int rmn = flow; // 剩余的流量
    for (int eg = cur[p]; eg && rmn; eg = edges[eg].next) // 如果已经没有剩余流量则退出
    {
        cur[p] = eg; // 当前弧优化,更新当前弧
        int to = edges[eg].to, vol = edges[eg].w;
        if (vol > 0 && lv[to] == lv[p] + 1) // 往层数高的方向增广
        {
            int c = dfs(to, min(vol, rmn)); // 尽可能多地传递流量
            rmn -= c; // 剩余流量减少
            edges[eg].w -= c; // 更新残余容量
            edges[eg ^ 1].w += c; // 再次提醒,链式前向星的cnt需要初始化为1(或-1)才能这样求反向边
        }
    }
    return flow - rmn; // 返回传递出去的流量的大小
}
inline int dinic()
{
    int ans = 0;
    while (bfs())
        ans += dfs();
    return ans;
}

ISAP

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=1009;

int pre[maxn],gap[maxn],h[maxn],cur[maxn];//pre记录是从哪一条边到达i的,gap记录在i层中有几个点,h记录i点的层次,cur当前弧优化。
int firs[maxn];//邻接表建边
struct node
{
    int  v,flow,nex;
}edge[maxn*maxn];
int N;
void build_edge(int u,int v,int flow)//建边
{
    edge[N]=(node){v,flow,firs[u]};
    firs[u]=N++;
    edge[N]=(node){u,0,firs[v]};
    firs[v]=N++;
}
void bfs(int s,int t)//广搜建层,不过这里是从汇点开始的,别弄错了,
{
    //初始化
    memset(h,-1,sizeof(h));
    memset(gap,0,sizeof(gap));
    queue<int>q;
    while(!q.empty())q.pop();

    q.push(t);
    h[t]=0;//将汇点定义为0层
    gap[0]=1;//0层点的个数加一
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=firs[u];i!=-1;i=edge[i].nex)
        {
            int v=edge[i].v;
            if(h[v]==-1)
            {
                h[v]=h[u]+1;
                gap[h[v]]++;//记录当前层的个数
                q.push(v);
            }
        }
    }
}
int isap(int s,int t,int n)//ISAP算法主体部分
{
    bfs(s,t);
    memcpy(cur,head,sizeof(head));
    memset(pre,0,sizeof(pre));//记录该点的前驱是谁(这里的前驱是指边,不是点)
    int ans=0,d,u=s;//ans记录最大流,d记录当前路径中的最小流,u记录当前查找到哪个点了。
    while(h[s]<n)//如果源点的层小于点的个数就可能还有流
    {
        int flag=1;//判断是否能够走,能否找到流。
        if(u==s) d=inf;//如果再次从源点出发,就初始化d,
        for(int i=cur[u];i!=-1;i=edge[i].nex)
        {
            int v=edge[i].v,flow=edge[i].flow;
            if(h[v]+1==h[u]&&flow)
            {
                cur[u]=i;
                pre[v]=i;//记录该点是由哪条边到达的
                u=v;//记录要去哪个点
                d=min(d,flow);//记录最小流
                flag=0;//能够往前走
                if(v==t)//如果走到了汇点
                {
                    while(u!=s)//原路返回,更新边的容量,与dinic的dfs思路一样
                    {
                        int j=pre[u];
                        edge[j].flow-=d;
                        edge[j^1].flow+=d;
                        u=edge[j^1].v;
                    }
                    ans+=d;//加上当前找到的流
                }
                break;
            }
        }
        if(flag)//如果没有找到下面的路,就更新点的层次
        {
            if(--gap[h[u]]==0) //如果该层上没有了点,说明没法继续查找了,结束。
                break;
            int min_=n-1;
            for(int i=firs[u];i!=-1;i=edge[i].nex)//查找与u相连的最小层,
            {
                int v=edge[i].v,flow=edge[i].flow;
                if(flow)
                    min_=min(min_,h[v]);
            }
            cur[u]=head[u];
            h[u]=min_+1;//重新给u建层
            gap[h[u]]++;//更新层的个数
            if(u!=s)//重要一步,如果当前点不是源点就要向后退一步,继续查找。
             u=edge[pre[u]^1].v;
        }
    }
    return ans;
}
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        memset(firs,-1,sizeof(firs));
        N=0;
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            build_edge(u,v,w);
        }
        printf("%d\n",isap(1,n,n));
    }
    return 0;
}

最小费用最大流

把最大流中的bfs改为spfa

int n, m, s, t, last[MAXN], inq[MAXN], dis[MAXN], cur[MAXN], vis[MAXN];
queue<int> Q;
bool SPFA()
{
    while (!Q.empty())
        Q.pop();
    memcpy(cur, head, sizeof(head));
    memset(last, -1, sizeof(last));
    memset(inq, 0, sizeof(inq));
    memset(dis, 63, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    dis[s] = 0;
    Q.push(s);
    while (!Q.empty())
    {
        int p = Q.front();
        Q.pop();
        inq[p] = 0;
        for (int e = head[p]; e != 0; e = edges[e].next)
        {
            int to = edges[e].to, vol = edges[e].w;
            if (vol > 0 && dis[to] > dis[p] + edges[e].c)
            {
                last[to] = e;
                dis[to] = dis[p] + edges[e].c;
                if (!inq[to])
                {
                    Q.push(to);
                    inq[to] = 1;
                }
            }
        }
    }
    return last[t] != -1;
}
int dfs(int p = s, int flow = INF)
{
    if (p == t)
        return flow;
    vis[p] = true;
    int rmn = flow;
    for (int eg = cur[p]; eg && rmn; eg = edges[eg].next)
    {
        cur[p] = eg;
        int to = edges[eg].to, vol = edges[eg].w;
        if (vol > 0 && !vis[to] && dis[to] == dis[p] + edges[eg].c)
        {                                 
            int c = dfs(to, min(vol, rmn));
            rmn -= c;                     
            edges[eg].w -= c;              
            edges[eg ^ 1].w += c;
        }
    }
    return flow - rmn;
}
int maxflow, mincost;
inline void MCMF()
{
    while (SPFA())
    {
        int flow = dfs();
        maxflow += flow;
        mincost += dis[t] * flow;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值