最大流(SAP)及最小费用最大流(SPFA)模版

来源:互联网 发布:守望先锋怎么看数据 编辑:程序博客网 时间:2024/06/06 05:28

最大流SAP算法模板dfs

//SAP算法#include<stdio.h>#include<string.h>#include<algorithm>#define inf 9999999#define M 1007#define MIN(a,b) a>b?b:a;using namespace std;struct E{    int v,w,next;}edg[500000];int dis[2000],gap[2000],head[2000],nodes;int sourse,sink,nn;void addedge(int u,int v,int w){    edg[nodes].v=v;    edg[nodes].w=w;    edg[nodes].next=head[u];    head[u]=nodes++;    edg[nodes].v=u;    edg[nodes].w=0;//或者w    edg[nodes].next=head[v];    head[v]=nodes++;}int dfs(int src,int aug){    if(src==sink)return aug;    int left=aug,mindis=nn;    for(int j=head[src];j!=-1;j=edg[j].next)    {    int v=edg[j].v;    if(edg[j].w)        {           if(dis[v]+1==dis[src])           {               int minn=MIN(left,edg[j].w);               minn=dfs(v,minn);               edg[j].w-=minn;               edg[j^1].w+=minn;               left-=minn;               if(dis[sourse]>=nn)return aug-left;               if(left==0)break;           }           if(dis[v]<mindis)           mindis=dis[v];        }    }        if(left==aug)        {            if(!(--gap[dis[src]]))dis[sourse]=nn;            dis[src]=mindis+1;            gap[dis[src]]++;        }        return aug-left;}int sap(int s,int e){    int ans=0;nn=e+1;    memset(dis,0,sizeof(dis));    memset(gap,0,sizeof(gap));    gap[0]=nn;    sourse=s;    sink=e;    while(dis[sourse]<nn)    ans+=dfs(sourse,inf);    return ans;}int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n,s=0,sum=0;        memset(head,-1,sizeof(head));        nodes=0;        建图        int anss=sap(s,t);        printf("%d\n",anss);    }    return 0;}

sap算法bfs版

#include<stdio.h>#include<string.h>#include<algorithm>#define MAXN 20010  //点#define MAXM 880010//边#define inf 0x3f3f3fusing namespace std;struct E{    int from,v,next;    int cap;} edge[MAXM];int num;int head[MAXN],dis[MAXN],gap[MAXN];int nn;//代表点的个数void init(){    num=0;    memset(head,-1,sizeof(head));}void addedge(int u,int v,int w){    edge[num].from=u;edge[num].v=v;edge[num].cap=w;    edge[num].next=head[u];head[u]=num++;    edge[num].from=v;edge[num].v=u;edge[num].cap=0;    edge[num].next=head[v];head[v]=num++;}void BFS(int start,int end){    memset(dis,-1,sizeof(dis));    memset(gap,0,sizeof(gap));    gap[0]=1;    int que[MAXN];    int front,rear;    front=rear=0;    dis[end]=0;    que[rear++]=end;    while(front!=rear)    {        int u=que[front++];        if(front==MAXN)front=0;        for(int i=head[u]; i!=-1; i=edge[i].next)        {            int v=edge[i].v;            if(dis[v]!=-1)continue;            que[rear++]=v;            if(rear==MAXN)rear=0;            dis[v]=dis[u]+1;            ++gap[dis[v]];        }    }}int SAP(int start,int end){    int res=0;    nn=end+1;    BFS(start,end);    int cur[MAXN];    int S[MAXN];    int vp=0;    memcpy(cur,head,sizeof(head));    int u=start;    int i;    while(dis[start]<nn)    {        if(u==end)        {            int temp=inf;            int inser;            for(i=0; i<vp; i++)                if(temp>edge[S[i]].cap)                {                    temp=edge[S[i]].cap;                    inser=i;                }            for(i=0; i<vp; i++)            {                edge[S[i]].cap-=temp;                edge[S[i]^1].cap+=temp;            }            res+=temp;            vp=inser;            u=edge[S[vp]].from;        }        if(u!=end&&gap[dis[u]-1]==0)            break;        for(i=cur[u]; i!=-1; i=edge[i].next)            if(edge[i].cap!=0&&dis[u]==dis[edge[i].v]+1)                break;        if(i!=-1)        {            cur[u]=i;            S[vp++]=i;            u=edge[i].v;        }        else        {            int min=nn;            for(i=head[u]; i!=-1; i=edge[i].next)            {                if(edge[i].cap==0)continue;                if(min>dis[edge[i].v])                {                    min=dis[edge[i].v];                    cur[u]=i;                }            }            --gap[dis[u]];            dis[u]=min+1;            ++gap[dis[u]];            if(u!=start)u=edge[S[--vp]].from;        }    }    return res;}int main(){    int s,t,n,m;    scanf("%d%d",&n,&m);    init();    s=0;    t=n+1;        int ans=SAP(s,t);    printf("%d\n",ans);    return 0;}


最小费用最大流 模版

一、最小费用最大流的模型
保证流量最大的前提下,所需的费用最小,这就是最小费用最大流问题.


 

带有费用的网络流图: G=(V,E,C,W)
V:顶点; E:弧;C:弧的容量;W:单位流量费用。
任意的弧<i,j>对应非负的容量c[i,j]和单位流量费用w[i,j]。满足:
① 流量f是G的最大流。
② 在f是G的最大流的前提下,流的费用最小。

 

F是G的最大流的集合(最大流不止一个):

在最大流中寻找一个费用最小的流 f.

 

二、最小费用最大流的算法
基本思路:
    把弧<i,j>的单位费用w[i,j]看作弧<i,j>的路径长度,每次找从源点s到汇点t长度最短(费用最小)的可增广路径进行增广。
1. 最小费用可增广路
2. 路径s到t的长度即单位流量的费用。

ps:是网络流EK算法的改进,在求增广路径的时候,把bfs改为带权的spfa,每次求权值最小的增广路。

ps:要注意一点,逆边cost[i][j] = -cost[j][i],不能忘了加上去。

 

 

自己的模板:邻接矩阵。#include<iostream>using namespace std; int n, ans;int cap[Max][Max], pre[Max];int cost[Max][Max], dis[Max];int que[Max];bool vis[Max]; bool spfa(){                  //  源点为0,汇点为n。    int i, head = 0, tail = 1;    for(i = 0; i <= n; i ++){        dis[i] = inf;        vis[i] = false;    }    dis[0] = 0;// dis 表示 最小 花费    que[0] = 0;    vis[u] = true;    while(tail != head){      //  循环队列。        int u = que[head];        for(i = 0; i <= n; i ++)            if(cap[u][i] && dis[i] > dis[u] + cost[u][i]){    //  存在路径,且权值变小。                dis[i] = dis[u] + cost[u][i];                pre[i] = u;                if(!vis[i]){                    vis[i] = true;                    que[tail ++] = i;                    if(tail == Max) tail = 0;                }            }        vis[u] = false;        head ++;        if(head == Max) head = 0;    }    if(dis[n] == inf) return false;    return true;} void end(){    int i, sum = inf;    for(i = n; i != 0; i = pre[i])        sum = min(sum, cap[pre[i]][i]);    for(i = n; i != 0; i = pre[i]){        cap[pre[i]][i] -= sum;        cap[i][pre[i]] += sum;        ans += cost[pre[i]][i] * sum;   //  cost[][]记录的为单位流量费用,必须得乘以流量。    }} int main(){    ....    ans = 0;    while(spfa()) end();    ....    return 0;}


 
不MLE的最小费用最大流模板:
#include<iostream>#include<algorithm>#include<cstring>#include<queue>#include<cstdio>using namespace std;const int MAXN=610*610*2+2;const int inf=1<<29;int pre[MAXN];          // pre[v] = k:在增广路上,到达点v的边的编号为kint dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为dint vis[MAXN];         // inq[u]:点u是否在队列中int path[MAXN];int head[MAXN];int NE,tot,ans,max_flow,map[666][666];struct node{    int u,v,cap,cost,next;} Edge[MAXN<<2];void addEdge(int u,int v,int cap,int cost){    Edge[NE].u=u;    Edge[NE].v=v;    Edge[NE].cap=cap;    Edge[NE].cost=cost;    Edge[NE].next=head[u];    head[u]=NE++;    Edge[NE].v=u;    Edge[NE].u=v;    Edge[NE].cap=0;    Edge[NE].cost=-cost;    Edge[NE].next=head[v];    head[v]=NE++;}int SPFA(int s,int t)                   //  源点为0,汇点为sink。{    int i;    for(i=s;i<=t;i++) dis[i]=inf;    memset(vis,0,sizeof(vis));    memset(pre,-1,sizeof(pre));    dis[s] = 0;    queue<int>q;    q.push(s);    vis[s] =1; while(!q.empty())        //  这里最好用队列,有广搜的意思,堆栈像深搜。    {        int u =q.front();        q.pop();        for(i=head[u]; i!=-1;i=Edge[i].next)        {            int v=Edge[i].v;            if(Edge[i].cap >0&& dis[v]>dis[u]+Edge[i].cost)            {                dis[v] = dis[u] + Edge[i].cost;                pre[v] = u;                path[v]=i;                if(!vis[v])                {                    vis[v] =1;                    q.push(v);                }            }        }        vis[u] =0;    }    if(pre[t]==-1)        return 0;    return 1;}void end(int s,int t){    int u, sum = inf;    for(u=t; u!=s; u=pre[u])    {        sum = min(sum,Edge[path[u]].cap);    }    max_flow+=sum;                          //记录最大流    for(u = t; u != s; u=pre[u])    {        Edge[path[u]].cap -= sum;        Edge[path[u]^1].cap += sum;        ans += sum*Edge[path[u]].cost;     //  cost记录的为单位流量费用,必须得乘以流量。    }}int main(){    int i,j,n,s,t;    while(scanf("%d",&n)!=EOF)    {        memset(head,-1,sizeof(head));        NE=ans=max_flow=s=0;                while(SPFA(s,t))        {            end(s,t);        }        printf("%d\n",ans);    }    return 0;}

spfa算法来求最短路径:
#include<algorithm>#include<cstring>#include<queue>#include<cstdio>#define MAXN 1000007#define inf 0x3f3f3f3fusing namespace std;long long dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为dint vis[MAXN];         // inq[u]:点u是否在队列中int head[MAXN];int NE,n,m;//n是点的数量,m是边的数量int a[MAXN],b[MAXN],c[MAXN];struct node{    int v,cap,next;} Edge[MAXN<<2];void addEdge(int u,int v,int cap){    Edge[NE].v=v;    Edge[NE].cap=cap;    Edge[NE].next=head[u];    head[u]=NE++;}void init(){     NE=0;     memset(head,-1,sizeof(head));     memset(vis,0,sizeof(vis));     for(int i=0;i<=n;i++) dis[i]=inf;}long long SPFA(int s)                   //  源点为0,汇点为sink。{    long long count=0;    queue<int>q;    q.push(s);    dis[s] = 0;    vis[s] =1;    while(!q.empty())        //  这里最好用队列,有广搜的意思,堆栈像深搜。    {        int u =q.front();        q.pop();vis[u]=0;        for(int i=head[u]; i!=-1;i=Edge[i].next)        {            int v=Edge[i].v;            if(dis[v]>dis[u]+Edge[i].cap)            {                dis[v]=dis[u]+Edge[i].cap;                if(!vis[v])                {                    q.push(v);                    vis[v]=true;                }            }        }    }    for(int i=1;i<=n;i++)//s到各个点最短路径之和        count+=dis[i];    return count;}int main(){    int cas;    scanf("%d",&cas);    while(cas--)    {        long long ans=0;        scanf("%d%d",&n,&m);        init();        for(int i=0;i<m;i++)        {            scanf("%d%d%d",&a[i],&b[i],&c[i]);            addEdge(a[i],b[i],c[i]);        }        ans+=SPFA(1);        printf("%lld\n",ans);    }    return 0;}


原创粉丝点击