精华内容
下载资源
问答
  • 最大流最小费用

    2013-05-29 17:37:25
    此程序可准确算出最大流最小费用,希望能给有需要的朋友带来帮助
  • c语言最大流最小费用

    2009-11-01 22:06:29
    最大流最小费用最大流最小费用最大流最小费用最大流最小费用最大流最小费用
  • 最大流最小费用数据

    2016-08-07 11:13:00
    最大流,最大流最小费用,完整代码及测试数据下载
  • 最大流,最大流最小费用算法,包括代码,测试数据,结果,已测试,完全正确。
  • 网络流算法 最大流 最小费用最大流算法的详细执行过程
  • 刚接触spfa算法。 我的理解是spfa算法实质是bellman算法的改进版,可以处理负值,这是和dij的本质区别,也是这里不能用dij的原因。...而最大流最小费用的思路说简单了就是最大流的实现时,以前没有最小费用这个约束

    刚接触spfa算法。

    我的理解是spfa算法实质是bellman算法的改进版,可以处理负值,这是和dij的本质区别,也是这里不能用dij的原因。

    spfa算法的队列的大小一定要注意!开小了悲情无限。

    而最大流最小费用的思路说简单了就是最大流的实现时,以前没有最小费用这个约束的时候,我们很随意的可以直接用bfs来搜索增广路径来解最大流。

    但此时有最小费用这个约束。我们用最短路来搜索增广路径。

    这样来求得的最大流则满足题意。

    #include<iostream>

    using namespace std;
    int n,m,k;
    const int inf=9999999;
    const int N=111;
    int shop[N][N];
    int storage[N][N];
    int mat[N][N];
    int cost[N][N];
    int ans;
    bool visit[N];
    int que[N*N],pre[N],dis[N];//que的大小设定不是简单的N,之前在广搜的时候习惯是N,上次一题就是直接写N,就一直TLE,今天才意识到,泪流满面。
    bool spfa()
    {
    int head=1,tail=1;
    for(int i=0;i<=n+m+1;i++)
    {
    dis[i]=inf;
    visit[i]=false;
    }
    dis[0]=0;
    que[tail++]=0;
    visit[0]=true;
    while(head<tail)
    {
    int now=que[head++];
    for(int i=0;i<=n+m+1;i++)
    if(mat[now][i]>0&&dis[i]>dis[now]+cost[now][i])
    {
    dis[i]=dis[now]+cost[now][i];
    pre[i]=now;
    if(!visit[i])
    {
    visit[i]=true;
    que[tail++]=i;
    //if
    }
    }
    visit[now]=false;

    }
    if(dis[n+m+1]==inf)
    return false;
    return true;
    }
    int main()
    {
    while(scanf("%d%d%d",&n,&m,&k),n!=0||m!=0||k!=0)
    {
    ans=0;
    int need[N];
    memset(need,0,sizeof(need));
    memset(shop,0,sizeof(shop));
    for(int i=1;i<=n;i++)
    for(int j=1;j<=k;j++)
    {
    scanf("%d",&shop[i][j]);
    need[j]+=shop[i][j];
    }
    int supply[N];
    memset(supply,0,sizeof(supply));
    memset(storage,0,sizeof(storage));
    for(int i=1;i<=m;i++)
    for(int j=1;j<=k;j++)
    {
    scanf("%d",&storage[i][j]);
    supply[j]+=storage[i][j];
    }
    bool flag=false;
    for(int i=1;i<=k;i++)
    {
    if(supply[i]<need[i])
    {
    flag=true;
    break;
    }
    }
    for(int t=1;t<=k;t++)
    {
    memset(mat,0,sizeof(mat));
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
    scanf("%d",&cost[j][m+i]);
    cost[m+i][j]=-cost[j][m+i];//这就是用spfa而不能用dij的原因
    mat[j][m+i]=inf;
    }
    if(flag)
    continue;
    for(int i=1;i<=m;i++)
    {
    mat[0][i]=storage[i][t];
    cost[0][i]=cost[i][0]=0;
    }
    for(int i=1;i<=n;i++)
    {
    mat[m+i][n+m+1]=shop[i][t];
    cost[m+i][n+m+1]=cost[n+m+1][m+i]=0;


    }
    while(spfa())
    {
    int min=inf;
    for(int i=m+n+1;i!=0;i=pre[i])
    {
    min=(min<mat[pre[i]][i]?min:mat[pre[i]][i]);
    }
    for(int i=n+m+1;i!=0;i=pre[i])
    {
    mat[pre[i]][i]-=min;
    mat[i][pre[i]]+=min;
    ans+=cost[pre[i]][i]*min;
    }
    }
    }
    if(flag)
    printf("-1\n");
    else
    printf("%d\n",ans);
    }
    return 0;
    }
    展开全文
  • 挺不错的一条最大流最小费用题,因为有重边,所以我们用邻接表来做,详情请看代码中的注释 /******************************************************************************* # Author :

    挺不错的一条最大流最小费用题,因为有重边,所以我们用邻接表来做,详情请看代码中的注释


    /*******************************************************************************
     # Author : Neo Fung
     # Email : neosfung@gmail.com
     # Last modified: 2011-10-10 19:47
     # Filename: ZOJ3362 Beer Problem.cpp
     # Description : 
     ******************************************************************************/
    // #include "stdafx.h"
    // #define DEBUG
    // 
    
    #include <fstream>
    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <string>
    #include <memory.h>
    #include <limits.h>
    #include <vector>
    #include <stack>
    #include <math.h>
    #define MAX 120
    using namespace std;
    
    struct NODE
    {
    	int to,cap,value,next;
    }node[MAX*MAX];
    
    int city[MAX];
    int cou,n,m;
    
    void init()
    {
    	memset(city,-1,sizeof(city));
    	cou=0;
    }
    
    void addEdge(int from,int to,int cap,int value)
    {
    	node[cou].to=to;
    	node[cou].cap=cap;
    	node[cou].value=value;
    	node[cou].next=city[from];
    	city[from]=cou++;
    
    	node[cou].to=from;
    	node[cou].cap=0;
    	node[cou].value=-value;
    	node[cou].next=city[to];
    	city[to]=cou++;
    }
    
    int EKwithSPFA(int source,int target)
    {
    	int value[MAX],pre_v[MAX],pre_e[MAX],instack[MAX];	//pre_v记录本节点的上一个节点,pre_e记录的是上一个节点到本节点的边的编号
    	int ans=0;
    	stack<int> mstack;
    
    	while(1)
    	{
    		//不断进行SPFA,找出残余路径中费用最小的路径
    		for(int i=source;i<=target;++i)
    			value[i]=INT_MIN;
    		value[source]=0;
    		memset(instack,0,sizeof(instack));
    		memset(pre_v,-1,sizeof(pre_v));	//全部指向无效节点
    		instack[source]=1;
    		mstack.push(source);	//我们用栈而不是队列可以减少时间
    		//spfa根据每条边的value求最短路
    		while(!mstack.empty())
    		{
    			int u=mstack.top();
    			mstack.pop();
    			instack[u]=0;
    			for(int i=city[u];i!=-1;i=node[i].next)
    			{
    				int v=node[i].to;
    				if(node[i].cap>0 && value[v]<value[u]+node[i].value)	//判断边还有没有余下空间,以及到底v节点后的利润能否增加
    				{
    					value[v]=value[u]+node[i].value;
    					pre_v[v]=u;
    					pre_e[v]=i;
    					if(!instack[v])
    					{
    						mstack.push(v);
    						instack[v]=1;
    					}
    				}
    			}
    		}
    		if(value[target]<=0)	//如果到了终点,利润是负数,则代表可以走的边都走完了,退出
    			break;
    		int a=INT_MAX;
    		for(int u=target;u!=source;u=pre_v[u])
    		{
    			int e=pre_e[u];
    			a=min(a,node[e].cap);	//找出这条通路的瓶颈
    		}
    		for(int u=target;u!=source;u=pre_v[u])
    		{
    			int e=pre_e[u];
    			node[e].cap-=a;
    			node[e^1].cap+=a;	//更新这条通路上所有边的容量,异或求反向边,偶数和1异或代表加1,奇数和1异或代表减1
    		}
    		ans+=value[target]*a;	//瓶颈乘以终点的费用就是新增的费用
    	}
    	return ans;
    }
    
    int main(void)
    {
    #ifdef DEBUG  
    	freopen("data.txt","r",stdin);  
    #endif  
    
    	int from,to,cap,value;
    	while(scanf("%d%d",&n,&m)!=EOF)
    	{
    		init();
    		for(int i=2;i<=n;++i)
    		{
    			scanf("%d",&value);
    			addEdge(i,n+1,INT_MAX,value);	//这里我们规定从目标城市卖出去的啤酒的价格是正数
    		}
    		for(int i=0;i<m;++i)
    		{
    			scanf("%d%d%d%d",&from,&to,&cap,&value);
    			addEdge(from,to,cap,-value);	//这里我们规定运输成本是负数
    			addEdge(to,from,cap,-value);	//不要忘了是双向边
    		}
    		int ans=EKwithSPFA(1,n+1);
    		printf("%d\n",ans);
    	}
    
    	return 0;
    }
    


    展开全文
  • 可以解决非完备匹配问题中的最大权匹配和最小权匹配,分别对应着最大费用最大流最小费用最大流 模板: 最大费用最大流: const int N=1e5+100;//点 const int M=1e5+100;//边 struct Edge { int to,w,cost,...

    主要是思维建边,建有向边,然后跑模板就行了

    可以解决KM算法所能解决的问题(完全取代)

    可以解决非完备匹配问题中的最大权匹配和最小权匹配,分别对应着最大费用最大流和最小费用最大流

    模板:

    最大费用最大流:

    const int N=1e5+100;//点
    
    const int M=1e5+100;//边
    
    struct Edge
    {
    	int to,w,cost,next;
    }edge[M];
    
    int head[N],cnt;
    
    void addedge(int u,int v,int w,int cost)
    {
    	edge[cnt].to=v;
    	edge[cnt].w=w;
    	edge[cnt].cost=cost;
    	edge[cnt].next=head[u];
    	head[u]=cnt++;
    	edge[cnt].to=u;
    	edge[cnt].w=0;
    	edge[cnt].cost=-cost;
    	edge[cnt].next=head[v];
    	head[v]=cnt++;
    }
    
    int d[N],incf[N],pre[N];
    
    bool vis[N];
    
    bool spfa(int s,int t)
    {
    	memset(d,0xcf,sizeof(d));
    	memset(vis,false,sizeof(vis));
        memset(pre,-1,sizeof(pre));
    	queue<int>q;
    	q.push(s);
    	vis[s]=true;
    	incf[s]=inf;
    	d[s]=0;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=head[u];i!=-1;i=edge[i].next)
    		{
    			int v=edge[i].to;
    			int w=edge[i].w;
    			int cost=edge[i].cost;
    			if(!w)
    				continue;
    			if(d[v]<d[u]+cost)
    			{
    				d[v]=d[u]+cost;
    				pre[v]=i;
    				incf[v]=min(incf[u],w);
    				if(!vis[v])
    				{
    					vis[v]=true;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return pre[t]!=-1;
    }
    
    int update(int s,int t)
    {
    	int x=t;
    	while(x!=s)
    	{
    		int i=pre[x];
    		edge[i].w-=incf[t];
    		edge[i^1].w+=incf[t];
    		x=edge[i^1].to;
    	}
    	return d[t]*incf[t];
    }
    
    void init()
    {
    	memset(head,-1,sizeof(head));
    	cnt=0;
    }
    
    int solve(int st,int ed)
    {
    	int ans=0;
    	while(spfa(st,ed))
    		ans+=update(st,ed);
    	return ans;
    }

    最小费用最大流:

    const int N=1e5+100;//点
    
    const int M=1e5+100;//边
    
    struct Edge
    {
    	int to,w,cost,next;
    }edge[M];
    
    int head[N],cnt;
    
    void addedge(int u,int v,int w,int cost)
    {
    	edge[cnt].to=v;
    	edge[cnt].w=w;
    	edge[cnt].cost=cost;
    	edge[cnt].next=head[u];
    	head[u]=cnt++;
    	edge[cnt].to=u;
    	edge[cnt].w=0;
    	edge[cnt].cost=-cost;
    	edge[cnt].next=head[v];
    	head[v]=cnt++;
    }
    
    int d[N],incf[N],pre[N];
    
    bool vis[N];
    
    bool spfa(int s,int t)
    {
    	memset(d,inf,sizeof(d));
    	memset(vis,false,sizeof(vis));
        memset(pre,-1,sizeof(pre));
    	queue<int>q;
    	q.push(s);
    	vis[s]=true;
    	incf[s]=inf;
    	d[s]=0;
    	while(!q.empty())
    	{
    		int u=q.front();
    		q.pop();
    		vis[u]=false;
    		for(int i=head[u];i!=-1;i=edge[i].next)
    		{
    			int v=edge[i].to;
    			int w=edge[i].w;
    			int cost=edge[i].cost;
    			if(!w)
    				continue;
    			if(d[v]>d[u]+cost)
    			{
    				d[v]=d[u]+cost;
    				pre[v]=i;
    				incf[v]=min(incf[u],w);
    				if(!vis[v])
    				{
    					vis[v]=true;
    					q.push(v);
    				}
    			}
    		}
    	}
    	return pre[t]!=-1;
    }
    
    int update(int s,int t)
    {
    	int x=t;
    	while(x!=s)
    	{
    		int i=pre[x];
    		edge[i].w-=incf[t];
    		edge[i^1].w+=incf[t];
    		x=edge[i^1].to;
    	}
    	return d[t]*incf[t];
    }
    
    void init()
    {
    	memset(head,-1,sizeof(head));
    	cnt=0;
    }
    
    int solve(int st,int ed)
    {
    	int ans=0;
    	while(spfa(st,ed))
    		ans+=update(st,ed);
    	return ans;
    }

     

    展开全文
  • 基于matlab的最大流最小费用代码 适于学习、修改、借鉴
  • 最大流最小费用流,着色的matlab以及Lingo的实现
  • 网络流相关定义 网络是一个有向带权图,包含一个源点和一个汇点,没反平行边 网络流:即网络上的流,...网络最大流:满足容量约束和流量守恒的条件下,在流网络中找到一个净输出最大的网络流 最大流 求解最大流

    网络流相关定义

    1. 网络是一个有向带权图,包含一个源点和一个汇点,没反平行边
    2. 网络流:即网络上的流,是定义在边集E上的一个非负函数 flow={flow(u,v)} , flow(u,v) 是边上的流量
    3. 可行流:满足容量约束流量守恒的网络流
    4. 网络最大流:满足容量约束流量守恒的条件下,在流网络中找到一个净输出最大的网络流

    最大流

    求解最大流的整体思路是福特福克森算法。该算法的思想是:
    在残余网络中找可增广路,然后在实流网络中沿着可增广路增流,在残余网络中沿着可增广路减流,;继续在残余网络中找可增广路。直到不存在可增广路位置。此时,实流网络中的可行流就是所求的最大流.

    增广路定理:设 flow 是网络 G 的一个可行流,如果不存在从源点S到汇点 t 关于flow的可增广路 p ,则flow G 的一个最大流

    福特福克森也是能算是一种求解最大流的思想,并不是具体的算法。下面说一下具体实现的算法。
    实现的算法有三种:EK算法,Dinic算法,SAP算法+gap优化

    我说一下EK算法和SAP算法。

    EK算法

    邻接矩阵存储残余网络,利用一个队列q来存放已访问未检查的点,vis[]数组来标记当前点是否被访问过,pre[]数组来记录前驱节点。

    利用BFS来寻找可增广路,当存在一条可增广路时。从汇点一直往前面找,一直找到起点,在路径上找到一条可增广量最小的边,然后从起点到汇点,残余网络中正向沿着可增广路增流,反向沿着可增广路减流,然后不断地进行BFS,把每一次的可增光量累加起来,就是最大流

    复杂度:O(VE2)

    代码:
    可以AC:POJ1273 Drainage Ditches(网络流–最大流,EK增广路算法)

    #include <cstring>
    #include <cctype>
    #include <stdlib.h>
    #include <string>
    #include <map>
    #include <iostream>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define inf 0x3f3f3f3f
    #define mem(a,b) memset(a,b,sizeof(a))
    const int N=200+20;
    int g[N][N],f[N][N],pre[N];//分别代表残余网络,实流网络,前驱数组
    bool vis[N];//标记数组
    int n,m;//点数和边数
    bool bfs(int s,int t)
    {
        mem(pre,-1);
        mem(vis,false);
        queue<int>q;
        vis[s]=true;
        q.push(s);
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            for(int i=1; i<=n; i++)
            {
                if(!vis[i]&&g[now][i]>0)
                {
                    vis[i]=true;
                    pre[i]=now;
                    if(i==t)
                        return true;
                    q.push(i);
                }
            }
        }
        return false;
    }
    int EK(int s,int t)
    {
        int v,w,d,maxflow=0;
        while(bfs(s,t))
        {
            v=t;
            d=inf;
            while(v!=s)
            {
                w=pre[v];
                d=min(d,g[w][v]);
                v=w;
            }
            maxflow+=d;
            v=t;
            while(v!=s)
            {
                w=pre[v];
                g[w][v]-=d;
                g[v][w]+=d;
                if(f[v][w]>0)
                    f[v][w]-=d;
                else
                    f[w][v]+=d;
                v=w;
            }
        }
        return maxflow;
    }
    int main()
    {
        int a,b,c;
        while(~scanf("%d%d",&m,&n))
        {
            mem(g,0);
            mem(f,0);
            for(int i=1; i<=m; i++)
            {
                scanf("%d%d%d",&a,&b,&c);
                g[a][b]+=c;
            }
            printf("%d\n",EK(1,n));
        }
        return 0;
    }

    SAP重贴标签算法

    在这个算法中,我们用邻接表存储混合网络(正向边显示,cap,flow,反向边也显示这个).

    这个算法的思想是,我们给广搜的网络标一个高度,每一层一个高度,最后一层的高度是0,然后按照广搜的层次,到起点依次递增。然后搜索的时候直接按照标签的告诉从高到低找,这样可以加快效率,当走的点的标签高度走不动的时候,我们采取重贴标签的策略,令当前节点的高度=所有邻接点高度的最小值+1,如果没有邻接边,则令当前节点的高度=节点数(注意:一定要保证容量>流量),然后不断的进行贴标签,重贴标签的过程,累积可增广量,最后的值就是最大流

    我们需要进行gap优化:利用一个数组g[]来记录,当前的高度的节点有多少个,当重贴标签后发现当前高度的点只有一个那么就可以提前结束程序

    算法复杂度: O(V2E)

    下面是代码:
    可以AC: P3376 【模板】网络最大流

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <stdlib.h>
    #include <string>
    #include <map>
    #include <iostream>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define inf 0x3f3f3f3f
    #define mem(a,b) memset(a,b,sizeof(a))
    const int N=10000+20;
    const int M=100000+20;
    int top;
    int h[N],pre[N],g[N];//h[i]记录每个节点的高度,pre[i]记录前驱,g[i]表示距离为i个节点数有多少个
    int first[N];
    struct node
    {
        int v,next;
        int cap,flow;
    } E[M*2];
    void init()
    {
        mem(first,-1);
        top=0;
    }
    void add_edge(int u,int v,int c)
    {
        E[top].v=v;
        E[top].cap=c;
        E[top].flow=0;
        E[top].next=first[u];
        first[u]=top++;
    }
    void add(int u,int v,int c)
    {
        add_edge(u,v,c);
        add_edge(v,u,0);
    }
    
    void set_h(int t)//标高函数,从终点向起点标高
    {
        queue<int>q;
        mem(h,-1);
        mem(g,0);
        h[t]=0;
        q.push(t);
        while(!q.empty())
        {
            int v=q.front();
            q.pop();
            g[h[v]]++;//当前高度的个数+1
            for(int i=first[v]; ~i; i=E[i].next)
            {
                int u=E[i].v;
                if(h[u]==-1)//当前节点未标高
                {
                    h[u]=h[v]+1;
                    q.push(u);
                }
            }
        }
    }
    
    int Isap(int s,int t,int n)//isap算法
    {
        set_h(t);
        int ans=0,u=s;
        int d;
        while(h[s]<n)//节点的高度小于顶点数
        {
            int i=first[u];
            if(u==s) d=inf;
            for(; ~i; i=E[i].next)
            {
                int v=E[i].v;
                if(E[i].cap>E[i].flow&&h[u]==h[v]+1)//容量大于流量且当前的高度等于要去的高度+1
                {
                    u=v;
                    pre[v]=i;
                    d=min(d,E[i].cap-E[i].flow);//找最小增量
                    if(u==t)//到达汇点
                    {
                        while(u!=s)
                        {
                            int j=pre[u];//找到u的前驱
                            E[j].flow+=d;//正向流量+d
                            E[j^1].flow-=d;//反向流量-d
                            u=E[j^1].v;//向前搜索
                        }
                        ans+=d;
                        d=inf;
                    }
                    break;
                }
            }
            if(i==-1)//邻接边搜索完毕,无法行进
            {
                if(--g[h[u]]==0)//重要的优化,这个高度的节点只有一个,结束
                    break;
                int hmin=n-1;//重贴标签的高度初始为最大
                for(int j=first[u]; ~j; j=E[j].next)
                {
                    if(E[j].cap>E[j].flow)
                        hmin=min(hmin,h[E[j].v]);//取所有邻接点高度的最小值
                }
                h[u]=hmin+1;//重新标高
                g[h[u]]++;//标高后该高度的点数+1
                if(u!=s)//不是源点时,向前退一步,重新搜
                    u=E[pre[u]^1].v;
            }
        }
        return ans;
    }
    int main()
    {
        int n,m,u,v,w,s,t;
        scanf("%d%d%d%d",&n,&m,&s,&t);
        init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        printf("%d\n",Isap(s,t,n));
        return 0;
    }

    kuangbin的SAP模板

    const int N=1000+20;
    const int M=2*100000+20;
    int top;
    int h[N],pre[N],g[N],first[N],cur[N];//h[i]记录每个节点的高度,pre[i]记录前驱,g[i]表示距离为i个节点数有多少个
    struct node
    {
        int v,next,cap;
    } E[M];
    void init()
    {
        mem(first,-1);
        top=0;
    }
    void add_edge(int u,int v,int c)
    {
        E[top].v=v;
        E[top].cap=c;
        E[top].next=first[u];
        first[u]=top++;
        E[top].v=u;
        E[top].cap=0;
        E[top].next=first[v];
        first[v]=top++;
    }
    int sap(int start,int end,int nodenum)
    {
        memset(h,0,sizeof(h));
        memset(g,0,sizeof(g));
        memcpy(cur,first,sizeof(first));
        int u=pre[start]=start,maxflow=0,aug=-1;
        g[0]=nodenum;
        while(h[start]<nodenum)
        {
    loop:
            for(int &i=cur[u]; i!=-1; i=E[i].next)
            {
                int v=E[i].v;
                if(E[i].cap&&h[u]==h[v]+1)
                {
                    if(aug==-1||aug>E[i].cap)
                        aug=E[i].cap;
                    pre[v]=u;
                    u=v;
                    if(v==end)
                    {
                        maxflow+=aug;
                        for(u=pre[u]; v!=start; v=u,u=pre[u])
                        {
                            E[cur[u]].cap-=aug;
                            E[cur[u]^1].cap+=aug;
                        }
                        aug=-1;
                    }
                    goto loop;
                }
            }
            int mindis=nodenum;
            for(int i=first[u]; i!=-1; i=E[i].next)
            {
                int v=E[i].v;
                if(E[i].cap&&mindis>h[v])
                {
                    cur[u]=i;
                    mindis=h[v];
                }
            }
            if((--g[h[u]])==0)break;
            g[h[u]=mindis+1]++;
            u=pre[u];
        }
        return maxflow;
    }

    最小费用最大流

    在求最大流的过程中,对于每一条边都有一个费用,现在希望费用最小,流最大。求最大流和最小费用。

    最小费用路算法

    最小费用路算法的思想是:先找最小费用路,在该路径上面增流,增加到最大流。

    利用邻接表建立双向边,正向边的花费为cost,反向边的花费为-cost.
    在找最小费用路的时候,从源点出发,沿着可行 (E[i].cap>E[i].flow)广度搜索每个邻接点, 如果当前的边可以继续松弛,那么就更新,就是SPFA算法,并且记录一下前驱。

    当找到最小费用路以后,那么就从汇点向源点找一条,最小的可增流量,沿着增广路正向增流,反向减流,最后花费的费用为:

    mincost=dis[V]d

    累加这个过程求出来的就是最小费用,最大流就是每次累加的可增流量

    可以AC:【模板】最小费用最大流
    算法复杂度: O(V2E)

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <stdlib.h>
    #include <string>
    #include <map>
    #include <iostream>
    #include <stack>
    #include <cmath>
    #include <queue>
    #include <vector>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define inf 1000000
    #define mem(a,b) memset(a,b,sizeof(a))
    const int N=5000+20;
    const int M=50000+20;
    
    int top;//当前边下标
    int dis[N],pre[N];//源点到点i的最小距离,pre[i]记录前驱
    bool vis[N];//标记数组
    int maxflow;
    int first[N];//存储头结点
    struct Edge
    {
        int v,next;
        int cap,flow,cost;
    } E[M*2];
    
    void init()
    {
        mem(first,-1);
        top=0;
        maxflow=0;
    }
    
    void add_edge(int u,int v,int c,int cost)
    {
        E[top].v=v;
        E[top].cap=c;
        E[top].flow=0;
        E[top].cost=cost;
        E[top].next=first[u];
        first[u]=top++;
    }
    void add(int u,int v,int c,int cost)
    {
        add_edge(u,v,c,cost);
        add_edge(v,u,0,-cost);
    }
    bool spfa(int s,int t,int n)
    {
        int i,u,v;
        queue<int>q;
        mem(vis,false);
        mem(pre,-1);
        for(int i=1; i<=n; i++) dis[i]=inf;
        vis[s]=true;
        dis[s]=0;
        q.push(s);
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            vis[u]=false;
            for(int i=first[u]; i!=-1; i=E[i].next)
            {
                v=E[i].v;
                if(E[i].cap>E[i].flow&&dis[v]>dis[u]+E[i].cost)
                {
                    dis[v]=dis[u]+E[i].cost;
                    pre[v]=i;
                    if(!vis[v])
                    {
                        q.push(v);
                        vis[v]=true;
                    }
                }
            }
        }
        if(dis[t]==inf)
            return false;
        return true;
    }
    int MCMF(int s,int t,int n)//minCostMaxFlow
    {
        int d;
        int i,mincost=0;//maxflow当前最大流量,mincost当前最小费用
        while(spfa(s,t,n))//表示找到了从s到t的最小费用路
        {
            d=inf;
            for(int i=pre[t]; i!=-1; i=pre[E[i^1].v]) //遍历反向边
                d=min(d,E[i].cap-E[i].flow);
            maxflow+=d;//更新最大流
            for(int i=pre[t]; i!=-1; i=pre[E[i^1].v]) //增广路上正向边流量+d,反向边流量-d
            {
                E[i].flow+=d;
                E[i^1].flow-=d;
            }
            mincost+=dis[t]*d;//dis[t]为该路径上单位流量费用之和
        }
        return mincost;
    }
    int main()
    {
        int n,m,st,ed;
        int u,v,w,c;
        scanf("%d%d%d%d",&n,&m,&st,&ed);
        init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d%d",&u,&v,&w,&c);
            add(u,v,w,c);
        }
        int mincost=MCMF(st,ed,n);
        printf("%d %d\n",maxflow,mincost);
        return 0;
    }
    

    最大流最小割定理

    主要是理解一些概念

    • 割:设Ci为网络N中一些弧的集合,若从N中删去Ci中的所有弧能使得从源点Vs到汇点Vt的路集为空集时,称Ci为Vs和Vt间的一个割。就是用一条线可以把一张有向图分割成两个不想交的集合,割的容量就是这一条线割到的边上的容量之和
    • 最小割:图中所有的割中,边权值和最小的割为最小割。就是用一条线把一个图分成两个部分,使得边上所有容量的和最小,如下图最小割的容量为:5+4+2=11
    • 最大流最小割定理:在任何的网络中,最大流的值等于最小割的容量。否则还可以增广。那么有一个特征:最小割个到的边的容量流量相等。
    展开全文
  • 最小费用最大流

    2011-12-16 00:33:24
    源程序,最大流最小费用,源代码,c++
  • 网络流-最小费用最大流总结。
  • 最小费用最大流】 【引用】 【镇楼】 天啦真的好好懂!!!麻麻再也不用担心我的网络流学习啦!!! 【引入】 首先,我们来看一个网络流图实例。 最大流问题:给定指定的一个有向图,其中有两个特殊的点...
  • 最小费用最大流模板

    2012-11-26 12:43:30
    最小费用最大流模板
  • 最小费用流问题,网络分析,最大流量最小费用流问题, 支撑树等一些列方法与例题。
  • 最小费用最大流问题matlab实现
  • 最小费用最大流C++算法,有最小费用算法,最大流算法,最短路算法,C++代码。
  • 最小费用最大流问题

    千次阅读 2019-07-04 11:11:13
    最小费用最大流问题 解决如下最小费用最大流问题。 查了很多资源发现用MATLAB操作的好用的不多,所以综合了一下很多资源,给出了自己的理解。 基本思路为:把各条弧上单位流量的费用当做距离,用Floyd算法求最...
  • 详细的介绍了最大流问题的基本概念与解法。 图文并茂地介绍Dinic算法原理与流程并且附上模板。 最小费用最大流的算法思想、想解例题和模板。
  • lingo最小费用最大流

    2010-04-29 11:28:07
    求下列网络的最小费用最大流,其中括号......
  • 1.什么是最小费用最大流?  打个比方,这是一个运输过程,每条道路连接两座城市,每条路都有限重,也要按照货物数量收取过路费,问最多可以运多少货物。 2.怎么算? 和最大流算法很像,唯一不同的是需要加一个...
  • 网络流 最小费用最大流问题

    千次阅读 2014-06-24 11:21:49
    //说明汇点不可达,辨明当前流已经是最小费用最大流 int a = INF; for(int i = t; i!=s; i = p[i]) a = (a>cost[p[i]][i]-flow[p[i]][i] ? cost[p[i]][i]-flow[p[i]][i] : a); for(int i = t; i!=s; i = p[i]){ ...
  • 最大/最小费用流(板子整理)

    千次阅读 2019-05-03 21:27:13
    板子(以院赛费用流裸题为例) 一般把超级源点s赋0,超级汇点t赋1+所有点的个数num 连边时调用add2函数,正向边流量满值且费用为正,负向边流量为0且费用为负 开点时点和边权的数组别开...最小费用最大流,正费用...
  • 【问题分析】 费用流问题。...求最小费用最大流最小费用流值就是最少运费,求最大费用最大流,最大费用流值就是最多运费。 【建模分析】 把每个仓库想象成一个中转站,由源点运来ai单位货物,运费为0,每个.
  • Java实现最小费用最大流问题

    万次阅读 多人点赞 2019-07-26 17:48:33
    最大流有多组解时,给每条边在附上一个单位费用的量,问在满足最大流时的最小费用是多少? 2 解决方案 下面代码所使用的测试数据如下图: package com.liuzhen.practice; import java.util.ArrayList; import ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,533
精华内容 10,613
关键字:

最大流最小费用