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

最大流SAP算法模板dfs

//SAP算法
#include
#include
#include
#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]

sap算法bfs版

#include
#include
#include
#define MAXN 20010  //点
#define MAXM 880010//边
#define inf 0x3f3f3f
using 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]edge[S[i]].cap)
                {
                    temp=edge[S[i]].cap;
                    inser=i;
                }
            for(i=0; idis[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:单位流量费用。
任意的弧对应非负的容量c[i,j]和单位流量费用w[i,j]。满足:
① 流量f是G的最大流。
② 在f是G的最大流的前提下,流的费用最小。

 

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

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

 

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

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

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

 

 

自己的模板:邻接矩阵。

#include
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
#include
#include
#include
#include
using namespace std;
const int MAXN=610*610*2+2;
const int inf=1<<29;
int pre[MAXN];          // pre[v] = k:在增广路上,到达点v的边的编号为k
int dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为d
int 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;
    queueq;
    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
#include
#include
#include
#define MAXN 1000007
#define inf 0x3f3f3f3f
using namespace std;
long long dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为d
int 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;
    queueq;
    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


你可能感兴趣的:(比赛&模板)