网络流(最大流,最小割)基础入门详解

本文详细介绍了网络流的基本概念,包括源点、汇点、容量和流量等,并解释了网络流的三大特性。通过实例演示最大流问题的解决方法及反向边的作用,最后阐述了最小割的概念及其与最大流的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网络流基本定义:

源点:有n个点,有m条有向边,有一个点很特殊,只出不进,叫做源点。

汇点:另一个点也很特殊,只进不出,叫做汇点。

容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量通常用c(u,v)表示,流量则通常是f(u,v).

残余网络:r(u,v) = c(u,v) – f(u,v),其中c(u,v) 表示容量,f(u,v)表示流量,r(u,v)表示残量网络

通常可以把这些边想象成道路,流量就是这条道路的车流量,容量就是道路可承受的最大的车流量。很显然的,流量<=容量。而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,所有“进入”他们的流量和等于所有从他本身“出去”的流量。

网络流的3个性质:

1、容量限制: f[u,v]<=c[u,v]

2、反对称性:f[u,v] = - f[v,u]

3、流量平衡: 对于不是源点也不是汇点的任意结点,流入该结点的流量和等于流出该结点的流量和。

只要满足这三个性质,就是一个合法的网络流.

最大流问题:

最大流问题,就是求在满足网络流性质的情况下,源点 s 到汇点 t 的最大流量。
问题:给出n个河流,m个点,以及每个河流的容量,求从1到m点的最大流量。

求解思路:

首先,假如所有边上的流量都没有超过容量(不大于容量),那么就把这一组流量,或者说,这个流,称为一个可行流
对于这种没有给出流量f的问题,称为零流问题,即所有的流量都是0的流。
(1).我们就从这个零流开始考虑,假如有这么一条路,这条路从源点开始一直一段一段的连到了汇点,并且,这条路上的每一段都满足流量<=容量。
(2).那么,我们一定能找到这条路上的每一段的流量的值当中的最小值flow。我们把这条路上每一段的流量都减去这个flow,一定可以保证这个流依然是可行流,这是显然的,然后再将这条路上的每一段的反向都加上这个flow
(3).然后将每次的流量都加上flow,这样我们就得到了一个更大的流,他的流量是之前的流量+flow,而这条路就叫做增广路。我们不断地从起点开始寻找增广路,每次都对其进行增广,直到源点和汇点不连通,也就是找不到增广路为止。
(4).当找不到增广路的时候,当前的流量就是最大流,这个结论非常重要。

注意事项:

1.反向边系列:
反向边求法:u->v的反向边f(v,u)=c(v,u)-f(v,u)=c(v,u)+f(u,v);
为什么要求反向边:在做增广路时可能会阻塞后面的增广路,或者说,做增广路本来是有个顺序才能找完最大流的。但我们是任意找的,为了修正,就每次将流量加在了反向弧上,让后面的流能够进行自我调整。
例如:下边这个例子1位源点,4为汇点
这里写图片描述
如果我们第一次找的是1->2->3->4这条增广路径可以得到一个可行流为1,执行操作(2)后图形变成了如下:
这里写图片描述
这时我们再找增广路径,你会发现已经无法从1到达4了,所以这个时候的最大流就是1了,但显然这是错误的,如果我们分别走1->2->3和1->3->4这条路就可以得到一个为2的可行流。
为什么会出现这样的错误呢?
那是因为我们找从1到4的路径是随机的没有任何技巧可言,并不能保证从1到4的某条路就是最大流,但可以确定的是这条路径很可能会破坏其他路径,而破坏的路径可能是构成最大流的其中一条路径。所以这个时候我们要给程序一个后悔的机会,即添加一条反向路径。具体看下边这个过程:
我们在找到1->2->3->4这条增广路径后需要修改原图如下:
这里写图片描述
这样修改图后,可以发现,这个时候还存在增广路径1->3->2->4,然后修改图如下:
这里写图片描述
加上之前的1->2->3->4,此时的最大流为2。
那么,这么做为什么会是对的呢?
事实上,当我们第二次的增广路走3-2这条反向边的时候,就相当于把2-3这条正向边已经是用了的流量给“退”了回去,不走2-3这条路,而改走从2点出发的其他的路也就是2-4。
如果这里没有2-4怎么办?
这时假如没有2-4这条路的话,最终这条增广路也不会存在,因为他根本不能走到汇点
同时本来在3-4上的流量由1-3-4这条路来“接管”。而最终2-3这条路正向流量1,反向流量1,等于没有流。
CODE:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f

using namespace std;

int Map[210][210];
int pre[210];
int flow[210];
int n,m;
int bfs(int s,int t)
{
    queue<int> Q;
    memset(pre,-1,sizeof(pre));
    pre[s] = 0;
    flow[s] = INF;
    Q.push(s);
    while(!Q.empty()){
        int index = Q.front();
        Q.pop();
        if(index == t)
            break;
        for(int i=1;i<=m;i++){
            if(i!=s && Map[index][i]>0&&pre[i]==-1){
                pre[i] = index;
                flow[i] = min(flow[index],Map[index][i]);
                Q.push(i);
            }
        }
    }
    if(pre[t] == -1)///到不了汇点
        return -1;
    else
        return flow[t];
}
int Max_flow(int s,int t)
{
    int inc=0,sumflow=0;
    while(bfs(s,t)!=-1){
        inc = bfs(s,t);
        int k = t;
        while(k!=s){
            int last = pre[k];
            Map[last][k] -= inc;
            Map[k][last] += inc;
            k = last;
        }
        cout<<"曾广:"<<inc<<endl;
        sumflow += inc;
    }
    return sumflow;
}
int main()
{
    int u,v,c;
    while(~scanf("%d %d",&n,&m)){
        memset(Map,0,sizeof(Map));
        memset(flow,0,sizeof(flow));
        for(int i=0;i<n;i++){
            scanf("%d %d %d",&u,&v,&c);
            if(u == v) continue;
            Map[u][v] += c;
        }
        cout<<Max_flow(1,m)<<endl;
    }
    return 0;
}
最小割

割的定义:设Ci为网络G中一些弧的集合,若从G中删去Ci中的所有弧能使得从源点Vs到汇点Vt的路集为空集时,称Ci为Vs和Vt间的一个割。(注意,必须是删除Ci中的所有边)
最小割:图中所有的割中,边权值和最小的割为最小割。

最小割与最大流的关系

我以下边这个例子来说明最大流和最小割之间的关系:
这里写图片描述
从1到4,中间经过2,3两节点,问此时的最大流是多少?
首先找一条从1到4的路径[1,2,4],该路径的最大流量是min(2,3)=2,因为[1,2]上面的容量已经被用了,所以路径[1,2,3,4]就行不通了,割去[1,2]后图变成了以下形式:
这里位 述
此时再找从1到4的路径[1,3,4],路径的最大流量是min(3,6)=3.割去[b,t]后,图如下:
这里写图片描述
此时就不存在从S到t的可行路径了,则结束最大流的查找。此时的最大流是2+3=5,被割的边容量和是2+3=5,即最大流=最小割。从这两个例子我们已经能理解最大流和最小割大体的含义了,也发现最大流的确和最小割是相等的从数值上来看。但只从这两个小例子就证明最大流和最小割相等是绝对不严格的,严格的数学证明可自行百度相关资料。

<think>好的,我现在需要帮助用户解决如何在Matlab中求线性方程组的基础解系的问题。首先,我得回忆一下基础解系的相关知识。基础解系是齐次线性方程组的解空间的基,也就是说,找到一组线性无关的解向量,使得任何解都可以表示为它们的线性组合。 用户提到他们想用Matlab来实现,所以我需要确定Matlab中有哪些函数或方法可以用来求解齐次方程组的基础解系。通常,解线性方程组可以用`null`函数,这个函数返回的是矩阵的零空间的一组标准正交基,也就是齐次方程Ax=0的基础解系的正交化后的形式。不过,用户可能需要的是基础解系本身,可能不需要正交化,所以可能需要进一步处理。 接下来,我需要考虑如何正确使用`null`函数。例如,对于矩阵A,使用null(A)会返回一个矩阵,其列向量就是基础解系的向量。这些向量已经是标准正交的,但用户可能需要的是更直观的解的形式,可能需要符号解而不是数值解,尤其是当矩阵的元素是符号时。这时候可能需要使用符号计算工具箱,比如用`sym`定义符号变量,然后用null函数处理符号矩阵。 另外,用户可能需要示例代码,所以我要准备一个具体的例子。比如,给定一个齐次方程组,写出对应的系数矩阵,然后用null函数求解,并解释结果的含义。同时,可能需要注意矩阵的秩,以及基础解系中向量的个数是否等于n - rank(A),其中n是变量的个数。 还有,用户提到的引用[1]中提到了QR分解、SVD分解等方法,但这里可能只需要基础的null函数应用。不过,可能需要简要提到这些方法,以保持回答的全面性。此外,要确保代码正确,比如在符号计算时,可能需要用`rref`(行简化阶梯形)来手动求解,但这可能比较复杂,所以推荐使用null函数。 最后,需要验证我的思路是否正确。比如,构造一个具体的齐次方程组,如: x1 + 2x2 + 3x3 = 0 4x1 + 5x2 + 6x3 = 0 对应的系数矩阵A是[1 2 3;4 5 6],用null(A)会返回一个3x1的列向量,因为n=3,rank(A)=2,所以基础解系的向量个数是1。这时候,解向量应该满足方程,可以用代入验证是否正确。 另外,如果用户的问题涉及到非齐次方程组,可能需要先求特解,再求齐次的基础解系,但用户的问题明确是基础解系,所以应集中在齐次的情况。 可能还需要注意Matlab中的数值计算精度问题,当矩阵接近奇异时,null函数的行为,但用户可能只需要基础用法。所以总结步骤应该是:构建系数矩阵,使用null函数,处理符号或数值的情况,给出示例代码,并解释结果的意义。</think>在MATLAB中求线性方程组的基础解系,可通过以下步骤实现: ### 方法步骤 1. **构造系数矩阵**:将齐次方程组的系数提取为矩阵$A$,例如方程组$A\bm{x}=\bm{0}$ 2. **使用`null`函数**:`null(A)`直接返回基础解系的正交基,若需符号解则用`sym(null(A))` 3. **验证解空间维度**:基础解系向量个数为$n - \text{rank}(A)$,其中$n$为未知数个数 ### 示例代码 ```matlab % 定义系数矩阵 A = [1 2 3; 4 5 6]; % 计算基础解系 basis = null(A, 'r'); % 'r'选项返回有理数形式的基础解系 disp('基础解系为:'); disp(basis); ``` ### 代码解析 - `null(A)`默认返回正交单位化的基础解系,添加`'r'`参数后输出未规范化的有理数形式解[^1] - 若需符号运算,需先定义符号矩阵: ```matlab syms x y z; A = [1 2 3; 4 5 6]; basis = null(sym(A)); ``` ### 验证方法 通过计算$A \cdot \text{basis}$应接近零矩阵(数值计算存在微小误差),例如: ```matlab error = A * basis; disp('验证误差:'); disp(error); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值