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

article/2025/9/28 19:48:38

摘要

今日,对最小费用最大流问题进行了一个简单的研究,并针对网上的一些已有算法进行了查找和研究。博客和资料很多,但是留下的算法很多运行失败、出错,或者意义不明。这里,个人对其中的Bellman-Ford、SPFA、改进的Dijkstra三种应用于最小费用最大流的算法进行了实现,经过测试,确保其可行性。

网络流

关于网络流,这里引入几个概念:

  • 源点:有n个点,有m条有向边,其中有一个点比较特殊,它只出不进,即入度为0。这样的点我们称为源点,一般用字母S表示。
  • 汇点:另一个点也比较特殊,只进不出,即出度为0。这样的点我们称为汇点,一般用字母T表示。
  • 容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量表示为c[i,j]表示,流量则用f[i,j]表示。

通常来说,我们可以将这些边具象成道路,流量就是这条道路上的车的容量,容量就是道路可以承受的最大的车流量。
很明显,流量≤容量
而对于每个不是源点和汇点的节点而言,可以类比为没有存储功能的货物的中转站。所有进入他们的流量等于所有从它出来的流量。

  • 最大流:把源点比作工厂的话,问题就是求工厂最大可以发出多少货物,而不至于超过道路的容量限制,也即,最大流问题。

求解思路:
首先,假如所有边上额流量都不超过容量,那么我们就把这一组流量,或者说,这个流,称为一个可行流
一个最简单的可行流的例子就是零流,即所有的流量都是0的流。

  1. 我们从这个零流开始考虑,假如有这样一条路径,这条路从原点开始一直一段一段的连接,就可以到达汇点。并且,这条路径上的每一段都会满足流量<容量(注意,不是≤,而是严格<)
  2. 那么,我们就一定可以找到这条路径上的每一段的(容量-流量)的值中的最小值delta。我们把这条路上的每一段的流量都加上这个delta,就一定可以保证目前这个流依然是可行流。
  3. 这样,我们就找到了一个更大的流,它的流量是之前的流量+delta,而这条路径就称为增广路径。我们不断地从起点开始寻找增广路径,每次都对其进行增广,直到源点和汇点不再连通,也就是找不到增广路径为止。
  4. 当我们找不到增广路径时,当前的流量就是最大流

补充:

  1. 寻找增广路径的时候我们可以简单的从原点开始做BFS,并不断修改这条路上的delta量,直到找到汇点或者找不到增广路径为止。
  2. 在程序实现的过程中,我们通常只是使用一个c数组来记录容量,而不是记录流量。当流量+delta时,我们可以通过容量-delta来实现,以方便程序编写。

增加反向边的目的:
在做增广路径时可能会阻塞后来的增广路径,换计划说,做增广路径本来是有一个顺序的,只有按照有这一顺序,才能知道最大流。
但是我们在寻找时是任意的,为了修正,我们就每次讲流量加入到了反向弧中从而让后面的流能够进行自我的调整。

例子

在这里插入图片描述
我们第一次,可以找到1-2-3-4这条增广路径,这条路径上的delta值显然为1。

此时,我们在修改之后得到了下面这个流。其中,边上的数字代表流量。

在这里插入图片描述
此时,边(1,2)和边(3,4)上的边就等于容量了,我们也再也找到不到其他的增广路径,于是,当前的流量是1。

然而,这个答案并不是最大流,因为我们可以同时走1-2-4和1-3-4,这样,可以得到流量为2的最大流。

之所以出现这样的问题,是因为我们在路径寻找的过程中,没有给一个“后悔”的机会,应该有一个不走2-3-4而改走2-4的机制。

而解决这个问题的办法,就是利用一种叫做反向边的概念来解决。
即每条边(i, j)都会有一条反向边(j,i),反向边也同样有它的容量。

在第一次找到增广路径之后,在把路径上每一段容量减少delta的同时,也把每一段上的反方向的容量增加delta。

c[x,y]-=delta;
c[y,x]+=delta;

就上面这个例子,当我们找到1-2-3-4这条增广路径之后,将容量修改如下:

在这里插入图片描述
此时我们再去寻找增广路径,就可以得到一条:1-3-2-4,将这条路径增广之后,得到最大流为2.

在这里插入图片描述

这样为何有效?
实际上,当我们第二次的增广路径走3-2这条反向边时,就相当于把2-3这条正向边已经用的流量给“退”了回去,不走2-3这条路,从而改走从2点出发的其他的路径也即是2-4。

而如果这里没有2-4怎么办?
这时,假如没有2-4这条道路,那么最终这条增广路径在生成过程中也不会存在,因为最终它根本无法到达汇点。
同时,本来在3-4上的流量则是由1-3-4来“接管”。而最终2-3这条路径正向流量为1,反向流量也为1,等于没有流。

最小费用最大流

对一个费用容量网络,具有相同流量f的可行流中,总费用最小的可行流称为该费用容量网络关于流量f的最小费用流。简称为流量为f的最小费用流

什么是最小费用最大流问题:
给定网络D=(V,A,C) 每一条弧(vi,vj)上,除了已给容量Cij外,还给了一个单位流量的费用b(vi,vj)>=0. 所谓最小费用最大流问题就是求一个最大流f,使流的总输送费用最小

对于例子:
在这里插入图片描述
从S出发,到达T,正确路径结果,为:

  • S->1->3->T
  • S->2->4->3->T
  • S->2->4->5->T

最大流为10,最小费用为84
以下算法在该图中测试,均可得出正确结果。

贝尔曼-福特算法(Bellman-Ford algorithm)

贝尔曼-福特算法(Bellman-Ford algorithm),是求解单元最短路径的一种算法。
它的基本原理是对图进行|V| - 1次松弛操作,得到所有可能的最短路径。
它比Dijkstra算法好的部分在于,在计算最短路径的班的权值可以为负,实现起来比较简单。
缺点则是时间复杂度较高,为O(|V||E|)。不过算法已经有了一些改进方案,比如队列优化的Bellmanford算法(SPFA算法),一定程度上提高了效率。

算法原理:
贝尔曼-福特算法与迪科斯彻算法类似,都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确的值替代,直至得到最优解。
在两个算法中,计算时每个边之间的估计距离值都比真实值大,并且被新找到路径的最小长度替代。
然而,迪科斯彻算法以贪心法选取未被处理的具有最小权值的节点,然后对其的出边进行松弛操作;而贝尔曼-福特算法简单地对所有边进行松弛操作,共**|V| - 1**次。
在重复地计算中,已计算得到正确的距离的边的数量不断增加,直到所有边都计算得到了正确的路径。
因为算法可以使用负权值的边,因此贝尔曼-福特算法比迪科斯彻算法适用于更多种类的输入。

优化选项:

  1. 循环的提前跳出
    在实际操作中,贝尔曼-福特算法经常会在未达到|V|-1前就给出解,|V|-1就是最大值。因此可以在循环中设置判定,在某次循环不再松弛时,直接退出循环,进行负权环判定。
  2. 队列优化
    队列优化的贝尔曼-福特算法——SPFA算法基本思路与原算法是一样的,不过该算法的提升在于它不会盲目尝试所有的节点,而是维护一个备选节点队列,并且仅有节点被松弛之后才会放入到队列中。

代码实现

#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <map>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>#define MAXN 5050
#define INF 0x3f3f3f3fusing namespace std;int n, m, s, t;
int u, v, c, w;
int maxFlow, minCost;struct Edge
{int from, to, flow, cap, cost;
};bool vis[MAXN];
int p[MAXN], a[MAXN], d[MAXN];
vector<int> g[MAXN];
vector<Edge> edges;void init(int n)
{for (int i = 0; i <= n; i++)g[i].clear();edges.clear();
}
void addedge(int from, int to, int cap, int cost)
{Edge temp1 = { from, to, 0, cap, cost };Edge temp2 = { to, from, 0, 0, -cost };//允许反向增广edges.push_back(temp1);edges.push_back(temp2);int len = edges.size();g[from].push_back(len - 2);g[to].push_back(len - 1);
}//贝尔曼-福特算法实现
bool bellmanford(int s, int t)
{for (int i = 0; i < MAXN; i++)d[i] = INF;d[s] = 0;memset(vis, false, sizeof(vis));memset(p, -1, sizeof(p));p[s] = -1;a[s] = INF;queue<int> que;que.push(s);vis[s] = true;while (!que.empty()){int u = que.front();que.pop();vis[u] = false;for (int i = 0; i < g[u].size(); i++){Edge& e = edges[g[u][i]];if (e.cap > e.flow&&d[e.to] > d[u] + e.cost)//进行松弛,寻找最短路径也就是最小费用{d[e.to] = d[u] + e.cost;p[e.to] = g[u][i];a[e.to] = min(a[u], e.cap - e.flow);if (!vis[e.to]){que.push(e.to);vis[e.to] = true;}}}}if (d[t] == INF)return false;maxFlow += a[t];minCost += d[t] * a[t];for (int i = t; i != s; i = edges[p[i]].from){edges[p[i]].flow += a[t];edges[p[i] ^ 1].flow -= a[t];}return true;
}void MCMF()
{while (bellmanford(s, t))continue;return;
}int _tmain(int argc, _TCHAR* argv[])
{cout << "节点数为:"; cin >> n;cout << "边数为:"; cin >> m;cout << "源点编号为:"; cin >> s;cout << "汇点编号为:"; cin >> t;cout << "输入 " << m << " 条边的信息:" << endl;while (m--){cout << "起点:"; cin >> u;cout << "终点:"; cin >> v;cout << "容量:"; cin >> c;cout << "费用:"; cin >> w;cout << "-----------------" << endl;addedge(u, v, c, w);}MCMF();cout << "最大流为:" << maxFlow << endl;cout<< "最小费用为"<<minCost << endl;cout << endl;system("pause");return 0;}

SPFA算法

算法描述:

  1. 初始化:distance数组(从源点s到各点的最小费用)全部赋值为inf,用一个队列保存所有待松弛的顶点,初始时将s点放入队列中。
  2. 队列+松弛操作:每次出队一个顶点u,对其所有的边进行松弛,如果存在某条边u->v松弛成功(dist(v)>dist(u)+w(u,v)),则将v加入队列中(当v不在队列时);重复以上操作直到队列为空或者发现负权环。
    如果网络中存在负权回路,则算法永远都不会结束,陷入死循环。
  • 判断是否存在负权环的方法:
    对任何一个顶点,每进入一次队列,意味着需要进行一次松弛,即如果某个顶点进入队列的次数超过V,说明存在负权环。

算法步骤:

  1. 建立一个队列,将源点加入队列中,建立一个数组dist记录源点到所有点的最短路径(初始为inf,源点到本身的最短路径是0)。
  2. 从队列中取出队头元素,刷新其连接的所有点的最短路径;如果刷新成功且被刷新点不在队列中,则把该点加入到队尾。
  3. 重复执行以上步骤直到队列为空或者队列中存在负权环。

代码实现

#include "stdafx.h"
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>#define MAXN 5050using namespace std;bool vis[MAXN];
int n, m, s, t;
int u, v, c, w;
int cost[MAXN], pre[MAXN], last[MAXN], flow[MAXN];
int maxFlow, minCost;
struct Edge
{int from, to, flow, cost;
}edge[MAXN];int head[MAXN], num_edge;queue <int> q;void addedge(int from, int to, int flow, int cost)
{edge[++num_edge].from = head[from];edge[num_edge].to = to;edge[num_edge].flow = flow;edge[num_edge].cost = cost;head[from] = num_edge;edge[++num_edge].from = head[to];edge[num_edge].to = from;edge[num_edge].flow = 0;edge[num_edge].cost = -cost;head[to] = num_edge;}bool SPFA(int s, int t)
{memset(cost, 0x7f, sizeof(cost));memset(flow, 0x7f, sizeof(flow));memset(vis, 0, sizeof(vis));q.push(s); vis[s] = 1; cost[s] = 0; pre[t] = -1;while (!q.empty()){int now = q.front();q.pop();vis[now] = 0;for (int i = head[now]; i != -1; i = edge[i].from){if (edge[i].flow>0 && cost[edge[i].to]>cost[now] + edge[i].cost){cost[edge[i].to] = cost[now] + edge[i].cost;pre[edge[i].to] = now;last[edge[i].to] = i;flow[edge[i].to] = min(flow[now], edge[i].flow);if (!vis[edge[i].to]){vis[edge[i].to] = 1;q.push(edge[i].to);}}}}return pre[t] != -1;
}void MCMF()
{while (SPFA(s, t)){int now = t;maxFlow += flow[t];minCost += flow[t] * cost[t];while (now != s){edge[last[now]].flow -= flow[t];edge[last[now] ^ 1].flow += flow[t];now = pre[now];}}
}int _tmain(int argc, _TCHAR* argv[])
{	memset(head, -1, sizeof(head)); num_edge = -1;//初始化 cout << "节点数为:"; cin >> n;cout << "边数为:"; cin >> m;cout << "源点编号为:"; cin >> s; cout << "汇点编号为:"; cin >> t; cout << "输入 " << m << " 条边的信息:" << endl;while (m--){cout << "起点:"; cin >> u; cout << "终点:"; cin >> v; cout << "容量:"; cin >> c; cout << "费用:"; cin >> w; cout << "-----------------" << endl;addedge(u, v, c, w);}MCMF();cout << "最大流为:" << maxFlow << endl;cout << "最小费用为:" << minCost << endl;cout << endl;system("pause");return 0;
}

改进的Dijkstra算法

算法描述:
用于求解指定两点间的最短路,或从指定点到其余个点的最短路。是目前求非负权网络最短路问题的最好方法。
基本步骤:

  1. 将所有的顶点分成两个集合,P集合和Q集合,初始时P集合只有源点,其他顶点都在Q集合中。
  2. 每次选择P集合中新加入的顶点u,用该顶点作为中转点更新Q集合中的顶点的最短路(松弛);选择Q中最短路值最小的顶点加入到集合P中。
  3. 重复步骤2直到集合Q中没有顶点。

由于最小费用最大流网络中存在负权值,Dijkstra算法不能直接求解最小费用最大流问题,如果最小费用最大流网络中的权值都非负,则可使用Dijkstra算法。引入势函数h(u)为上一次Dijkstra算法的dist(u)(表示从源点到顶点u的最短距离),对每一条边(u,v),h(v)<=h(u)+w(u,v)成立,则下一次计算中dist(v)=dist(u)+w(u,v)+h(u)-h(v),所有的dist值必然都大于等于0,则可以继续用Dijkstra算法求解最短路。

代码实现:

#include "stdafx.h"
#include <iostream>
#include <algorithm>
#include <queue>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <functional>#define MAXN 5050
#define INF 0x3f3f3f3f
#define P pair<int,int>using namespace std;struct edge
{int to, cap, cost, rev;
};int n, m, s, t;
int u, v, c, w;
int maxFlow, minCost;vector<edge> G[MAXN];
int h[MAXN];
int dist[MAXN], prevv[MAXN], preve[MAXN];void addedge(int from, int to, int cap, int cost)
{edge temp1 = { to, cap, cost, (int)G[to].size() };edge temp2 = { from, 0, -cost, (int)G[from].size() - 1 };G[from].push_back(temp1);G[to].push_back(temp2);
}//Dijkstra算法实现
void MCMF(int s, int t, int f)
{fill(h + 1, h + 1 + n, 0);while (f > 0){priority_queue<P, vector<P>, greater<P> > D;memset(dist, INF, sizeof dist);dist[s] = 0; D.push(P(0, s));while (!D.empty()){P now = D.top(); D.pop();if (dist[now.second] < now.first) continue;int v = now.second;for (int i = 0; i<(int)G[v].size(); ++i){edge &e = G[v][i];if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];prevv[e.to] = v;preve[e.to] = i;D.push(P(dist[e.to], e.to));}}}if (dist[t] == INF) break;for (int i = 1; i <= n; ++i) h[i] += dist[i];int d = f;for (int v = t; v != s; v = prevv[v])d = min(d, G[prevv[v]][preve[v]].cap);f -= d; maxFlow += d;minCost += d * h[t];for (int v = t; v != s; v = prevv[v]){edge &e = G[prevv[v]][preve[v]];e.cap -= d;G[v][e.rev].cap += d;}}
}int _tmain(int argc, _TCHAR* argv[])
{cout << "节点数为:"; cin >> n;cout << "边数为:"; cin >> m;cout << "源点编号为:"; cin >> s;cout << "汇点编号为:"; cin >> t;cout << "输入 " << m << " 条边的信息:" << endl;while (m--){cout << "起点:"; cin >> u;cout << "终点:"; cin >> v;cout << "容量:"; cin >> c;cout << "费用:"; cin >> w;cout << "-----------------" << endl;addedge(u, v, c, w);}MCMF(s, t, INF);cout << "最大流为:" << maxFlow << endl;cout << "最小费用为" << minCost << endl;cout << endl;system("pause");return 0;
}

算法对比

名称特点不足
Bellman-Ford可以解决负权边,但不允许有负环每次循环值均对所有元素进行松弛判断,造成许多不必要的操作。
SPFA进阶版的BF,使用队列进行优化,每次循环值选择当前节点相邻的若干节点进行松弛。在稀疏图上十分高效单路增广。SPFA需要维护较为复杂的标号和队列操作,同时为了修正标号,需要不止一次地访问某些节点,速度会比较慢。
改进的Dijkstra速度普遍比SPFA要快。无法直接处理负权边图,需要对算法进行改进。

补充

除了上述三种算法之外,还有诸如Dinic、ZKW等算法,不过个人没有研究,这里就不再赘述了。

参考文献

[1] 最小费用最大流(详解+模板)
[2] 数据结构与算法分析 - 网络流入门(Network Flow)
[3] 最小费用最大流问题
[4] 维基百科-最小费用最大流问题
[5]【最小费用最大流】知识点讲解
[6] P3381 【模板】最小费用最大流 题解


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

相关文章

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

举个例子&#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)

IDEA重新生成 iml 文件

IDEA中的.iml文件是项目标识文件&#xff0c;缺少了这个文件&#xff0c;IDEA就无法识别项目。 使用命令重新生成iml文件&#xff1a; mvn idea:module