网络流(2)-----最小费用最大流(附带讲解SPFA算法)

article/2025/9/28 14:57:52

一.最小费用最大流(简称费用流)概念

1.什么是费用流问题

上篇文章我们讲解了最大流问题,那什么是最小费用最大流呢?听名字就可以看出,我们要在满足最大流的同时找到达成最大流的最小费用。对于一个网络流,最大流是一定的,但是组成最大流的费用是可以不同的,这里就有了在最大流网络上产生的费用流网络,就有了最小花费问题。

2.引例(洛谷 3381)

题目描述:

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。

输入格式:

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含四个正整数ui、vi、wi、fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi。

输出格式:

一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5                                       50 280

二.算法分析

1.EK算法 + SPFA 最短路

(1)思路分析

<1>只要是网络流的增广路思想,我们都要不断去寻找增广路。寻找增广路时我们可以使用DFS,BFS。但是增广路往往是曲折而漫长的,有时候我们可能走了错误的路径,因为我们的目的是到达汇点,但是中间有些路会使我们距离汇点更远,或者走到了不通的路,从而使得路径要重新寻找,所以我们在求最大流的时候使用了BFS来找增广路,距离汇点越来越近。DFS很容易TLE   QAQ

<2>在费用流中,我们仍要首先满足最大流的概念,那就必须仍要不断寻找增广路。但是为了让费用最小,那我们每次寻找的增广路都希望是花费最小的路。那么这里我们就可以使用最短路的思想来寻找增广路,每次优先使用花费最小的路径。因为图中反向建弧时,我们费用也要变成相反的(回流的时候相当于把钱拿回来),这就产生了负边问题,所以使用SPFA来求最短路。

<3>我们用每边单位流量的花费作为边权,假如一条增广路上每条边的花费分别为 c1,c2,.......ck , 那么这条边的最小流量为flow,则此增广路花费为 : c1 * flow + c2*flow + ..... + ck*flow = (c1+ c2 + c3 + .... + ck)*flow = Sum(ci)*flow

这里的Sum(ci)就是我们要求的最短路!

 

(2)SPFA算法求最短路(会的请略过。。QAQ)

<1>Dijkstra算法为什么无法求负权图

Dijkstra算法使用的是贪心的思想,每次在当前边中,选一条最短边并且标记该边最短距离已经确定。但是当存在负权边时,当前最短的边不一定就是最短距离,因为下一次拓展加上负权边时可能会变得更小,所以在负权图中,当前最短边不一定是到该点的最短距离,与Dijkstra的算法思想矛盾,因此Dijkstra不适用负权图。如下图(来自某博客。。忘记哪个了,博主看到轻戳我)

<2>SPFA求最短路思路

摒弃了Dijkstra算法的选择当前最短边的思路,相反来想,既然存在负权边,那么最好的当然是用负权边去更新其他边,那么被负权边更新的其他边又可以用更小的距离去更新其他其他边,所以说由于负权边的存在,我们可以不断的用被更新的边去更新其他边,直到所有边都不再改变为止。(如果有负权无向边可能就成负权环了。。只能用于有向图??)

<3>SPFA算法代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 100 +7;
struct Edge{int to,next,val;
}edge[maxn*100];
int head[maxn],n,m,tot,dist[maxn];
bool vis[maxn];
void addEdge(int a,int b,int c){edge[tot].to = b;edge[tot].next = head[a];edge[tot].val = c;head[a] = tot++;
}
void SPFA(int s){memset(vis,0,sizeof(vis));memset(dist,INF,sizeof(dist));queue<int> que;dist[s] = 0;que.push(s);while(!que.empty()){int u = que.front();que.pop();vis[u] = false;//取消当前点标记for(int i = head[u];~i;i = edge[i].next){int v = edge[i].to;if(dist[v] > dist[u] + edge[i].val){//如果连接点能更新dist[v] = dist[u] + edge[i].val;//更新if(!vis[v]){//如果未被标记,则标记入队,可以以更小的距离去更新其他边vis[v] = true;que.push(v);}}}}
}
int main()
{while(scanf("%d%d",&n,&m)!=EOF&&(n||m)){tot = 0;memset(head,-1,sizeof(head));for(int i = 0;i<m;i++){int a,b,v;scanf("%d%d%d",&a,&b,&v);addEdge(a,b,v);}SPFA(1);if(dist[n]==INF)printf("Nothing\n");else printf("%d\n",dist[n]);}return 0;
}

 

(3)费用流  EK最大流 + SPFA最短路:

用SPFA求增广路 + EK算法求最大流

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 5000 + 7;
struct Edge{int from,to,next,cap,flow,cost;
}edge[maxn*20];
int head[maxn],pre[maxn],dist[maxn],n,m,tot,s,t;
bool vis[maxn];
void addEdge(int a,int b,int c,int cost){//注意反向弧的负权值edge[tot].from = a;edge[tot].to = b;edge[tot].next = head[a];edge[tot].cap = c;edge[tot].flow = 0;edge[tot].cost = cost;head[a] = tot++;edge[tot].from = b;edge[tot].to = a;edge[tot].next = head[b];edge[tot].cap = 0;edge[tot].flow = 0;edge[tot].cost = -cost;head[b] = tot++;
}
bool SPFA(){//SPFA求增广路,dist[t]保存最小花费memset(dist,INF,sizeof(dist));memset(vis,0,sizeof(vis));queue<int> que;dist[s] = 0;vis[s] = 1;que.push(s);while(!que.empty()){int u = que.front();que.pop();vis[u] = 0;for(int i = head[u];~i;i = edge[i].next){int v = edge[i].to;if(edge[i].cap > edge[i].flow && dist[v] > dist[u] + edge[i].cost){dist[v] = dist[u] + edge[i].cost;pre[v] = i;if(!vis[v]){vis[v] = 1;que.push(v);}}}}if(dist[t]!=INF)return true;return false;
}
int CostFlow(int &flow){//EK算法int mincost = 0;while(SPFA()){//能找到增广路int Min = INF;for(int i = t;i!=s;i = edge[pre[i]].from){//寻找最小流Min = min(Min,edge[pre[i]].cap - edge[pre[i]].flow);}for(int i = t;i!=s;i = edge[pre[i]].from){//处理所有边edge[pre[i]].flow+=Min;edge[pre[i]^1].flow-=Min;}flow+=Min;mincost+=(dist[t]*Min);//累和最小花费}return mincost;
}
int main()
{int T;scanf("%d",&T);while(T--){tot = 0;memset(head,-1,sizeof(head));scanf("%d%d%d%d",&n,&m,&s,&t);for(int i = 0;i<m;i++){int a,b,c,cost;scanf("%d%d%d%d",&a,&b,&c,&cost);addEdge(a,b,c,cost);}int MaxFlow = 0;int MinCost = CostFlow(MaxFlow);printf("%d %d\n",MaxFlow,MinCost);}return 0;
}

 


http://chatgpt.dhexx.cn/article/Uh7ZgpBR.shtml

相关文章

最小费用最大流问题详解

最小费用最大流问题 一、问题描述 在网络中求一个最大流f&#xff0c;使流的总输送费用最小。 b ( f ) ∑ ( v i , v j ) b i j f i j b(f) \sum\limits_{(v_i,v_j)} b_{ij} f_{ij} b(f)(vi​,vj​)∑​bij​fij​ ) &#xff08; b i j b_{ij} bij​ 表示弧 ( v i , v j…

最小费用最大流问题与算法实现(Bellman-Ford、SPFA、Dijkstra)

摘要 今日&#xff0c;对最小费用最大流问题进行了一个简单的研究&#xff0c;并针对网上的一些已有算法进行了查找和研究。博客和资料很多&#xff0c;但是留下的算法很多运行失败、出错&#xff0c;或者意义不明。这里&#xff0c;个人对其中的Bellman-Ford、SPFA、改进的Di…

如何区分冲突域和广播域?

举个例子&#xff0c;原始社会的人都生活在山洞里&#xff0c;每个人都住在自己的山洞里&#xff0c;洞穴的中间只有一个狭长的走廊可以出去&#xff0c;平时出去玩&#xff0c;大哥都会喊一嗓子&#xff0c;有人出去打猎不&#xff0c;所有人都听得到&#xff0c;这就是一个广…

冲突域和广播域区别

一、区别 1、广播域就是说如果站点发出一个广播信号后能接收到这个信号的范围。通常来说一个局域网就是一个广播域。冲突域指一个站点向另一个站点发出信号。除目的站点外&#xff0c;有多少站点能收到这个信号。这些站点就构成一个冲突域。 2、冲突域通过集线器连接&#xf…

冲突域和广播域区别,集线器、交换机和路由器对比

冲突域 我们把以太网想象为对讲机&#xff0c;电脑想象为使用对讲机的人&#xff0c;数据传输想象为使用对讲机说话。现在一群人打真人CS&#xff0c;两个及以上的人同时通过对讲机说话&#xff0c;就听不清在说什么了&#xff0c;这就是冲突。对讲机通道只能一人单独使用。对…

冲突域和广播域; 集线器、交换机以及路由器比较

转自https://blog.csdn.net/gui951753/article/details/79402528#1%E5%B7%A5%E4%BD%9C%E5%B1%82%E6%AC%A1%E4%B8%8D%E5%90%8C 目录 冲突域和广播域 联网中继设备 集线器&#xff08;hub&#xff09; 交换机(switch) 路由器(route) 三者的异同 冲突域和广播域 在介绍这三…

交换机基础原理,冲突域和广播域

交换机的基本定义 提供了大量的接入端口&#xff0c;能够很好的满足大量用户接入到网络中的需求。 在OSI模型的二层&#xff0c;数据链路层&#xff1b; 可以识别数据包中的MAC地址信息&#xff0c;根据MAC地址进行转发数据&#xff0c;并且会将这些MAC地址与对应的端口记录在…

冲突域和广播域的区分

一、概念理解&#xff1a; 1、冲突域&#xff08;物理分段&#xff09;&#xff1a; 连接在同一导线上的所有工作站的集合&#xff0c;或者说是同一物理网段上所有节点的集合或以太网上竞争同一带宽的节点集合。这个域代表了冲突在其中发生并传播的区域&#xff0c;这个区域可…

冲突域和广播域的区别

冲突域 冲突域指的是会产生冲突的最小范围&#xff0c;在计算机和计算机通过设备互联时&#xff0c;会建立一条通道&#xff0c;如果这条通道只允许瞬间一个数据报文通过&#xff0c;那么在同时如果有两个或更多的数据报文想从这里通过时就会出现冲突了。 冲突域的大小可以衡…

冲突域和广播域

冲突域&#xff1a; 冲突域是信道的争抢&#xff0c;信道只有一条&#xff0c;A在使用&#xff0c;B就不可以使用&#xff0c;这样就会产生冲突&#xff0c;这就是冲突域的产生 在一个信道上的所有主机组成一个冲突域&#xff0c;划分网络越小越好&#xff0c;不会引起很大的冲…

如何理解冲突域和广播域?(转)

如何理解冲突域和广播域&#xff1f;&#xff08;转&#xff09; 转自&#xff1a;http://blog.sina.com.cn/s/blog_7e8dec240102wyio.html 网上看到的好文章&#xff0c;整理下来&#xff0c;方便复习&#xff0c;侵删 1、冲突域&#xff1a; 【定义】在同一个冲突域中的每…

设备划分冲突域和广播域

冲突域是一种物理分段&#xff0c;指连接到同一导线上所有工作站的集合、同一物理网段上所有节点的集合或是以太网上竞争同一带宽节点的集合。冲突域表示冲突发生并传播的区域&#xff0c;这个区域可以被认为是共享段。在OSI模型中&#xff0c;冲突域被看作是OSI第一层的概念&a…

HCIA—冲突域与广播域(详解 + 区别)

目录 一、冲突域&#xff1a; &#xff08;1&#xff09;HUB组网&#xff1a; &#xff08;2&#xff09;冲突域 概述 特点&#xff1a; 二、广播域&#xff1a; &#xff08;1&#xff09;广播域 概述特点&#xff1a; 三、冲突域 与 广播域的区别与联系&#xff1a; 一…

网络基础之冲突域和广播域

温故&#xff1a; 前面的几天 &#xff0c;主要讲了OSI网络七层的相关知识&#xff0c;着重的分析了每一层所具备的功能和服务&#xff0c;在这里我再次强调一遍&#xff1a;功能指的是本层的作用&#xff0c;服务指的是本层能为上层提供的业务&#xff0c;至于协议则是定义了每…

广播域和冲突域

在准备软考的时候将广播域看成了广域网,结果就错了一道题,这篇文章就从这道错题开始... 1.广播域和冲突域的定义 广播域:网络中能接受任一设备发出的广播帧的设备的集合. 冲突域:在同一个网络内,如果任意两台计算机在同时通信是会发生冲突,那么它们所组成的网络就是一个冲突域…

计算机网络 —— 冲突域和广播域

一、概念 &#xff08;1&#xff09;冲突域 定义&#xff1a;同一时间内只能有一台设备发送信息的范围。 分层&#xff1a;基于OSI的第一层物理层 设备&#xff1a;第二层设备能隔离冲突域&#xff0c;比如Switch。交换机能缩小冲突域的范围&#xff0c;交换接的每一个端口就…

冲突域与广播域

冲突域是一种物理分段&#xff0c;指连接到同一导线上所有工作站的集合、同一物理网段上所有节点的集合或是以太网上竞争同一带宽节点的集合。冲突域表示冲突发生并传播的区域&#xff0c;这个区域可以被认为是共享段。在OSI模型中&#xff0c;冲突域被看作是OSI第一层的概念&a…

向上取整的代码写法

如何实现向上取整 例如&#xff0c;x / n 上取整&#xff0c;代码如下 v (x (n - 1)) / n 下取整呢&#xff1f;haha v x / n 例题 森林中&#xff0c;每个兔子都有颜色。其中一些兔子&#xff08;可能是全部&#xff09;告诉你还有多少其他的兔子和自己有相同的颜色。我们…

php函数向上取整,php向上取整用什么函数

我们经常用到的PHP取整函数&#xff0c;主要是&#xff1a;ceil&#xff0c;floor&#xff0c;round&#xff0c;intval。 ceil -- 进一法取整说明float ceil ( float value ) 返回不小于 value 的下一个整数&#xff0c;value 如果有小数部分则进一位。ceil() ... 在PHP中&…

PHP取整的方法

变量$num 1.直接取整&#xff0c;舍弃小数&#xff0c;保留整数 intval($num) 2.四舍五入取整 round($num) 3.向上取整&#xff08;不管小数点后面是什么都会累加1&#xff09; ceil($num) 4.向下取整 floor($num)