【深圳大学算法设计与分析】 实验六 最大流应用问题 FF -> EK -> Dinic

目录

一、实验目的:

二、内容:棒球赛问题

三、实验要求

四、提交要求

————————

问题分析解释:

————————

算法简解:

Ford–Fulkerson 增广

Edmonds–Karp 算法

Dinic算法

Dinic和EK的区别:

代码:


一、实验目的:

  1. 掌握最大流算法思想。
  2. 学会用最大流算法求解应用问题。

二、内容:棒球赛问题

网络流和棒球赛淘汰问题 | Matrix67: The Aha Moments

1996 年 9 月 10 日,《旧金山纪事报》的体育版上登载了《巨人队正式告别 NL 西区比赛》一文,宣布了旧金山巨人队输掉比赛的消息。当时,圣地亚哥教士队凭借 80 场胜利暂列西区比赛第一,旧金山巨人队只赢得了 59 场比赛,要想追上圣地亚哥教士队,至少还得再赢 21 场比赛才行。然而,根据赛程安排,巨人队只剩下 20 场比赛没打了,因而彻底与冠军无缘(摘自http://www.matrix67.com/blog/archives/5190)。

  有趣的是,报社可能没有发现,其实在两天以前,也就是 1996 年 9 月 8 日,巨人队就已经没有夺冠的可能了。那一天,圣地亚哥教士队还只有 78 场胜利,与洛杉矶道奇队暂时并列第一。此时的巨人队仍然是 59 场胜利,但还有 22 场比赛没打。因而,表面上看起来,巨人队似乎仍有夺冠的可能。然而,根据赛程安排

,圣地亚哥教士队和洛杉矶道奇队互相之间还有 7 场比赛要打,其中必有一方会获得至少 4 场胜利,从而拿到 82 胜的总分;即使巨人队剩下的 22 场比赛全胜,也只能得到 81 胜。由此可见,巨人队再怎么努力,也不能获得冠军了。

  在美国职业棒球的例行赛中,每个球队都要打 162 场比赛(对手包括但不限于同一分区里的其他队伍,和同一队伍也往往会有多次交手),所胜场数最多者为该分区的冠军;如果有并列第一的情况,则用加赛决出冠军。在比赛过程中,如果我们发现,某支球队无论如何都已经不可能以第一名或者并列第一名的成绩结束比赛,那么这支球队就提前被淘汰了(虽然它还要继续打下去)。从上面的例子中可以看出,发现并且证明一个球队已经告败,有时并不是一件容易的事。

关于这个事情有一个有趣的故事,下面是一段对话:

“看到上周报纸上关于爱因斯坦的那篇文章了吗?……有记者请他算出三角比赛的数学公式。你知道,一支球队赢得了很多剩余的比赛,其他球队则赢这个赢了那个。这个比赛到底有多少种可能性?哪个球队更有优势?”

“他到底知道吗?”

“显然他知道的也不多。上周五他选择道奇队没有选巨人队。”

上面的表是四个球队的比赛情况,现在的问题是哪些球队有机会以最多的胜利结束这个赛季?可以看到蒙特利尔队因最多只能取得 80 场胜利而被淘汰,但亚特兰大队已经取得 83 场胜利,蒙特利尔队因为wi + ri < wj 而被淘汰。费城队可以赢83场,但仍然会被淘汰。 。 。如果亚特兰大输掉一场比赛,那么其他球队就会赢一场。所以答案不仅取决于已经赢了多少场比赛,还取决于他们的对手是谁。

请利用最大流算法给出上面这个棒球问题的求解方法。

三、实验要求

1. 解释流网络的构造原理。

2. 解释为什么最大流能解决这个问题。

3.给出上面四个球队的求解结果。

4. 尽可能实验优化的最大流算法。

四、提交要求

1. 在blackboard提交电子版实验报告,注意实验报告的书写,整体排版。

2. 实验报告的实验步骤部分需详细给出算法思想与实现代码之间的关系解释,不可直接粘贴代码(直接粘贴代码者视为该部分内容缺失)。

3. 实验报告中要求证明该算法的关键定理,并说明这些定理所起的作用。

4. 源代码作为实验报告附件上传。

5. 在实验课需要现场运行验证并讲解PPT。

————————

问题分析解释:

如果把每个单位的流量理解成一个一个的胜局,那么网络流也就可以理解为这些胜局的来源和去向。


编号:

要判断一个队伍是否有机会以最多的胜利结束这个赛季,只需让他全胜,然后分配其他队伍,看能否是最大值。

即在其余队伍都不超过这只队伍最高得分的前提下,比赛能否比完,如果能比完,这支队伍确实有机会获胜。

如果比不完(即此时无论如何分配,不管谁赢,都会超过这只队伍),那么这支队伍就无法取胜。所以我们可以用最大流来解决这个问题

让Atl接下来比赛全胜:

可以流完。

————————

算法简解:

最大流 - OI Wiki

一条从源点 s 到汇点 t 的路径称为增广路

对于一条增广路,给每一条边 都加上等量的流量,以令整个网络的流量增加,这一过程被称为增广。

最大流的求解可以被视为若干次增广分别得到的流的叠加。

—————

Ford–Fulkerson 增广

Ford–Fulkerson 增广是计算最大流的一类算法的总称。该方法运用贪心的思想,通过寻找增广路来更新并求解最大流。

步骤:

1.寻找任意一条增广路

2.取增广路中最小容量作为本次增广流量

3.添加反向边——负流量 (反向边容量为0,此时 $$容量 - 负流量$$ 为正值)

时间复杂度:

上界:总边数*求得的最大流

对于反向边的理解:

我们当前的增广并不是最优的,需要调整,调整即把原来走过去的一部分“反悔”重新选择路径。反向边实则实现了这样的方式——别的增广路也能走这里,那么之前的可以换个路径走。所以可以走更多的增广路。

Edmonds–Karp 算法

13-3: Edmonds-Karp Algorithm 寻找网络最大流_哔哩哔哩_bilibili

Edmonds–Karp 算法其实就是优化了 Ford–Fulkerson 增广,在最开始找增广时,不去随意找,而是用Bfs找最短的。

时间复杂度为O(|V||E|)

迭代次数:在最坏的情况下,每次 BFS 都会减少至少一条边的容量,因此算法的迭代次数不会超过边数 E。

由于每次 BFS 的时间复杂度是 O(V + E),并且迭代次数最多是 E,因此 Edmonds-Karp 算法的总时间复杂度是 O(E * (V + E))

Dinic算法

13-4: Dinic's Algorithm 寻找网络最大流_哔哩哔哩_bilibili

分层概念:

BFS 来构建分层图。

Dinic:

时间复杂度最坏为 O(m*n*n)

最多需要n-1轮,每轮BFS+DFS是O(m*n)

Dinic 算法会重复执行 BFS 和 DFS,直到无法找到增广路径为止。在最坏的情况下,每次迭代都会减少至少一条边的容量,因此迭代次数不会超过 E。

Dinic和EK的区别:

Dinic比EK更优,是因为Dinic一次分层后会把一层的增广路径都给找到。

代码:

直接粘的OI WIKI的板子:

#include <bits/stdc++.h>
using namespace std;
const int INF = std::numeric_limits<int>::max();

#define maxn 250
#define N 250
#define INF 0x3f3f3f3f
#define ll long long

struct Edge {
    int from, to, cap, flow;

    Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
struct EK {
    int n, m;             // n:点数,m:边数
    vector<Edge> edges;   // edges:所有边的集合
    vector<int> G[maxn];  // G:点 x -> x 的所有边在 edges 中的下标
    int a[maxn], p[maxn];  // a:点 x -> BFS 过程中最近接近点 x 的边给它的最大流
                           // p:点 x -> BFS 过程中最近接近点 x 的边

    void init(int n) {
        for (int i = 0; i < n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    int Maxflow(int s, int t) {
        int flow = 0;
        for (;;) {
            memset(a, 0, sizeof(a));
            queue<int> Q;
            Q.push(s);
            a[s] = INF;
            while (!Q.empty()) {
                int x = Q.front();
                Q.pop();
                for (int i = 0; i < G[x].size(); i++) {  // 遍历以 x 作为起点的边
                    Edge& e = edges[G[x][i]];
                    if (!a[e.to] && e.cap > e.flow) {
                        p[e.to] = G[x][i];  // G[x][i] 是最近接近点 e.to 的边
                        a[e.to] =
                            min(a[x], e.cap - e.flow);  // 最近接近点 e.to 的边赋给它的流
                        Q.push(e.to);
                    }
                }
                if (a[t]) break;  // 如果汇点接受到了流,就退出 BFS
            }
            if (!a[t])
                break;  // 如果汇点没有接受到流,说明源点和汇点不在同一个连通分量上
            for (int u = t; u != s;
                u = edges[p[u]].from) {  // 通过 u 追寻 BFS 过程中 s -> t 的路径
                edges[p[u]].flow += a[t];      // 增加路径上边的 flow 值
                edges[p[u] ^ 1].flow -= a[t];  // 减小反向路径的 flow 值
            }
            flow += a[t];
        }
        return flow;
    }
};
struct MF {
    struct edge {
        int v, nxt, cap, flow;
    } e[N];

    int fir[N], cnt = 0;

    int n = 249, S = 1, T = 12;
    ll maxflow = 0;
    int dep[N], cur[N];

    void init() {
        memset(fir, -1, sizeof fir);
        cnt = 0;
    }

    void addedge(int u, int v, int w) {
        e[cnt] = { v, fir[u], w, 0 };
        fir[u] = cnt++;
        e[cnt] = { u, fir[v], 0, 0 };
        fir[v] = cnt++;
    }

    bool bfs() {
        queue<int> q;
        memset(dep, 0, sizeof(int) * (n + 1));

        dep[S] = 1;
        q.push(S);
        while (q.size()) {
            int u = q.front();
            q.pop();
            for (int i = fir[u]; ~i; i = e[i].nxt) {
                int v = e[i].v;
                if ((!dep[v]) && (e[i].cap > e[i].flow)) {
                    dep[v] = dep[u] + 1;
                    q.push(v);
                }
            }
        }
        return dep[T];
    }

    int dfs(int u, int flow) {
        if ((u == T) || (!flow)) return flow;

        int ret = 0;
        for (int& i = cur[u]; ~i; i = e[i].nxt) {
            int v = e[i].v, d;
            if ((dep[v] == dep[u] + 1) &&
                (d = dfs(v, min(flow - ret, e[i].cap - e[i].flow)))) {
                ret += d;
                e[i].flow += d;
                e[i ^ 1].flow -= d;
                if (ret == flow) return ret;
            }
        }
        return ret;
    }

    void dinic() {
        while (bfs()) {
            memcpy(cur, fir, sizeof(int) * (N));
            maxflow += dfs(S, INF);
        }
    }
} mf;
int main() 
{
    int n;
    cin >> n;
    mf.init();
    for (int i = 0; i < n; i++)
    {
        int f, t, c;
        cin >> f >> t >> c;
        mf.addedge(f, t, c);
    }
    mf.dinic();
    cout << "最大流:" << mf.maxflow << endl;
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/763495.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32第十四课:低功耗模式和RTC实时时钟

文章目录 需求一、低功耗模式1.睡眠模式2.停止模式3.待机模式 二、RTC实现实时时钟1.寄存器配置流程2.标准库开发3.主函数调用 三、需求实现代码 需求 1.实现睡眠模式、停止模式和待机模式。 2.实现RTC实时时间显示。 一、低功耗模式 电源对电子设备的重要性不言而喻&#xff…

【程序大侠传】异步架构应用回调数据接收接口偶发NPE

前序 在这片浩瀚的代码江湖中&#xff0c;各大门派林立&#xff0c;各自修炼独门绝技&#xff0c;江湖中的侠士们分别担任着开发、测试、产品和运维的角色&#xff0c;共同守护着这片数字化的疆域。 开发门派&#xff1a;代码剑宗 代码剑宗的弟子们精通各种编程语言&#xff…

【嵌入式】探索嵌入式世界:在ARM上构建俄罗斯方块游戏的奇妙之旅

文章目录 前言&#xff1a;1. 简介2. 总体设计思路及功能描述2.1 设计思路2.2 功能描述2.3 程序流程图 3. 各部分程序功能及详细说明3.1 游戏界面函数3.1.1 游戏界面中的图片显示3.1.2 游戏开始界面3.1.3 游戏主界面3.1.4 游戏结束广告界面3.1.5 游戏界面中的触摸反馈3.1.6 游戏…

【Spring Boot】基于 JPA 开发的文章管理系统(CRUD)

基于 JPA 开发的文章管理系统&#xff08;CRUD&#xff09; 1.实现文章实体2.实现数据持久层3.实现服务接口和服务接口的实现类3.1 创建服务接口3.2 编写服务接口的实现 4.实现增、删、改、查的控制层 API 功能4.1 获取文章列表4.2 根据 id 获取文章对象4.3 新增4.4 保存4.5 删…

第三届环境工程与可持续能源国际会议(EESE 2024)

随着全球气候变化和环境问题日益严峻&#xff0c;环境工程与可持续能源领域的研究和发展显得尤为重要。第三届环境工程与可持续能源国际会议&#xff08;EESE 2024&#xff09;作为这一领域的重要交流平台&#xff0c;将于2024年10月25日至27日在湖南长沙盛大召开。本次会议将汇…

算法实验2.2、2.3

2.2主要内容 比较快速排序&#xff0c;归并排序以及堆排序算法的时间效率。了解影响算法执行时间的 主要因素以及如何降低算法的执行时间。 #include<iostream> using namespace std; #include<stdio.h> #include<malloc.h> #include<stdlib.h> #inc…

vue全局方法plugins/utils

一、在src目录下创建一个plugins文件夹 test.ts文件存放创建的方法&#xff0c;index.ts用于接收所有自定义方法进行统一处理 二、编写自定义方法 // test.ts文件 export default {handleTest(val1: number, val2: number) {// 只是一个求和的方法return val1 val2;}, };三…

MySQL数据库的主从复制与读写分离

一、MySQL数据库的主从复制 1.主从复制的概述及原理 &#xff08;1&#xff09;主从复制的意义 在实际的生产环境中&#xff0c;如果对数据库的读和写都在同一个数据库服务器中操作,无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此&#xff…

【Nvidia+AI相机】涂布视觉检测方案专注提高锂电池质量把控标准

锂电池单元的质量在多个生产制造领域都至关重要&#xff0c;特别是在新能源汽车、高端消费电子等行业。这些领域的产品高度依赖锂电池提供持续、稳定的能量供应。优质的锂电池单元不仅能提升产品的性能和用户体验&#xff0c;还能确保使用安全。因此&#xff0c;保证锂电池单元…

微信小程序template模板引入

如图&#xff1a;temp.wxml是template引入的模板 在two.wxml中&#xff1a; import&#xff1a;是引入temp的页面让template中的内容显示出来在two页面中&#xff1b; include:是显示temp页面内容不在template包裹&#xff0c;template以外的view标签文字和不在view的文字让…

探索PcapPlusPlus开源库:网络数据包处理与性能优化

文章目录 0. 本文概要1. PcapPlusPlus介绍1.1 概述1.2主要特性和功能1.3 PcapPlusPlus 主要模块关系和依赖1.4 网络协议层处理过程 2. 实例2.1 基于 PcapPlusPlus 的应用程序设计和封装流程&#xff1a;2.2 多线程示例代码2.3 代码说明&#xff1a; 3. 程序性能进一步优化3.1 避…

Golang内存分配

Go内存分配语雀笔记整理 Golang内存模型设计理念思考核心代码阅读mspanmcachemcentral中心缓存mheap分配过程 Golang内存模型设计理念思考 golang内存分配基于TCmalloc模型&#xff0c;它核心在于&#xff1a;空间换时间&#xff0c;一次缓存&#xff0c;多次复用&#xff1b;…

基于x86+FPGA+AI轴承缺陷视觉检测系统,摇枕弹簧智能检测系统

一、承缺陷视觉检测系统 应用场景 轴类零件自动检测设备&#xff0c;集光、机、软件、硬件&#xff0c;智能图像处理等先进技术于一体&#xff0c;利用轮廓特征匹配&#xff0c;目标与定位&#xff0c;区域选取&#xff0c;边缘提取&#xff0c;模糊运算等算法实现人工智能高…

Linux 高级编程——线程控制

线程控制&#xff1a;互斥与同步 概念&#xff1a; 互斥 》在多线程中对临界资源的排他性访问。 互斥机制 》互斥锁 》保证临界资源的 访问控制。 pthread_mutex_t mutex; 互斥锁类型 互斥锁变量 内核对象 框架&#xff1a; 定义互斥锁 》初始化锁 》加…

Kafka-服务端-副本同步-源码流程

杂 在0.9.0.0之前&#xff0c;Kafka提供了replica lag.max.messages 来控制follower副本最多落后leader副本的消息数量&#xff0c;follower 相对于leader 落后当超过这个数量的时候就判定该follower是失效的&#xff0c;就会踢出ISR&#xff0c;这里的指的是具体的LEO值。 对…

Hadoop权威指南-读书笔记-01-初识Hadoop

Hadoop权威指南-读书笔记 记录一下读这本书的时候觉得有意思或者重要的点~ 第一章—初识Hadoop Tips&#xff1a; 这个引例很有哲理嘻嘻&#x1f604;&#xff0c;道出了分布式的灵魂。 1.1 数据&#xff01;数据&#xff01; 这一小节主要介绍了进入大数据时代&#xff0c;面…

【windows|012】光猫、路由器、交换机详解

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

QML学习——Qt Quick Extras Examples 1.4(八)

Qt Quick Extras Examples 阅读官方的源码然后尝试做了下 01 A car dashboard 样例演示&#xff1a; 说明&#xff1a; ValueSource组件控制数值相关的动画&#xff0c;例如图中数值的变化&#xff1b;TurnIndicator组件是控制左右方向灯的闪烁和背景&#xff0c;里面使用…

excel修改批量一列单价的金额并保留1位小数

1.打开表格&#xff0c;要把单价金额变成现在的两倍&#xff0c;数据如下&#xff1a; 2.把单价这一列粘贴到一个新的sheet页面&#xff0c;在B2单元格输入公式&#xff1a;A2*2 然后按enter回车键,这时候吧鼠标放到B2单元格右下角&#xff0c;会出现一个黑色的小加号&#xf…