石油网络的放大器设置问题

本文探讨了一个涉及石油传输网络的问题,网络由加权有向无环图表示,其中源点压力最大,压力随距离降低。目标是在网络中放置最少的压力放大器以确保石油能传输到所有位置。介绍了两种解决方案:BFS贪心算法和DFS分支定界法,并分析了它们的时间和空间性能。同时,提供了数据集的描述、格式以及不同方法的实现细节。
摘要由CSDN通过智能技术生成

1.问题描述

一个石油传送网络可由一个加权有向无环图G表示。该图中有一个称为源点的顶点S ( 保证S的入度为0 ) , 从S出发 , 石油流向图中的其他顶点 。G中每条边上的权重为它所连接的两点间的距离。

在输送石油的过程中 , 需要有一定的压力才能使石油从一个点到达另一个点,但压力会随着路程的增加而降低 ( 即压力的损失量是路程的函数 ) 。

因此为了保证石油在网络的正常运输,在网络传输中必须保证在任何位置的压力都要不小于最小压力Pmin。为了维持这个最小压力,可在G中的一些或全部顶点放置压力放大器 , 压力放大器可以将压力恢复至该网络允许的最大压力Pmax。

可以设d为石油从压力Pmax降为压力Pmin所走的距离 , 在无压力放大器的情况下石油可运输的距离不超过d .

在该问题中 , 需要在G中放置最少的压力放大器 , 使得该传输网络从S出发,能够将汽油输送到图中的所有位置

2.相关假设

①为了简化计算 , 每走一个单位长度就会降低一个单位压力且Pmin=0 , 即d=Pmax - Pmin = Pmax
② 起点S处的压力为Pmax
③如果存在一个点x , x有多条入边e[1]…e[n] , x处的压力p[x] = max(p[x], p[e[i].from] - e[i].cost)即x处的压力为到达x处的压力中的最大值
图中每条边上的任何一点都要能到达: 例如 如果存在一个点x , x处的压力>Pmin , 但存在一条边 e , p[e.from] - e.cost < Pmin 这也是不合法的。

3.基本要求

①给出两种方法以解决上述问题,思考如何验证两种方法的正确性.
②比较两种方法的时间和空间性能,用图表显示比较结果。

4.数据集描述

①该数据集提供100组石油网络的数据 , 可自行选择进行测试 , 输入数据在input文件夹下, 标准输出数据在outputSTD文件夹下
②在Image文件夹下存放了每组结果的可视化图片 , 标注了边权 , 压力 , 是否放置放大器 . (注 : 仅供参考 , 可能存在多种方案)
③该数据集保证加权有向无环图 , 保证只有一个入度为0的源点
④根目录下的make.py为数据生成器 , 数据生成器依赖于库cyaron , 你可以阅读该文档以了解其使用方法和原理 cyaron并没有指出生成的dag只有一个源点 , 但他的写法确实只有一个源点

5. 数据集格式

输入数据 :

第一行包含三个整数 [n, m, l] , 源点均为 1 号节点

  • n 表示节点个数
  • m 表示边的个数
  • l 表示最大边权 ( 可以认为是Pmax )

接下来 m 行 , 每行三个正整数[x, y, c]用来标识一条有向边

  • x 表示起点
  • y 表示终点
  • c 表示边权

输出数据 :

一行一个整数 , 表示最少的放大器的个数


题解

在这里插入图片描述
以图形化输出solve12.jpg为例

输入建立了从结点i到j的多条路径,其中有用的是最短路径最长路径。用Pi 表示nodei 的压力,对于边nodei -> nodej,nodej 的压力等于nodei 的压力减去路程耗费cost。初始时只有node1的压力是确定的,其它所有结点都根据石油运送过程中的路径损失、压力放大器的设置点来更新。

根据题意要求,对此题分别按最短边和最长边建图,nodei -> nodej 的最短边用来更新nodej 的压力,最长边用来作边界条件,检测该放大器设置是否合理。即,在保证最长边(最大cost)能到达的前提下,用最小边(最小cost)来更新nodej 的压力。

例如solve12.jpg,如果 node7 不放增压器,pre7 = 19,虽然pre8 的值是用 node6 -> node8 的耗费来更新的,但此时 node7 无法到达 node8 ,因此 node7 必须要放增压器。

方法

本题方法众多,已了解到的同学们使用的方法就包括枚举、剪枝、prim、Floyd、分支定界等,在此介绍利用bfs贪心利用dfs分支定界两种方法。

①bfs贪心:
首先,对于所给的图,记录它的拓扑序列,把源点 node1 插入到队列中,按照拓扑序列对图进行bfs遍历。
例如solve12.jpg:
在这里插入图片描述
按照插入边的顺序,它的拓扑序列为:[1, 2, 3, 4, 5, 6, 7, 8]。用p来表示当前结点的压力大小。此时检查1,由于1是源点,故p = Pmax = 51。然后,检查所有与1相邻接的点,即2、3、6,由于1到2、3、6的最大花费的边都能够成功到达,因此1不必放加压器。于是,用最小花费的边来更新2、3、6结点的压力值。此时pre2 = 1,pre3 = 11,pre6 = 8。

然后考察2,此时p = 1。与2相邻接的点有4、5、6,按照结点序列先检查4,从2可以到达4,更新pre4 = 0;但发现从2不可以到达5,于是给结点2放上加压器,重新按照序列检查、更新2能到达的所有结点,也就是再从4开始更新一遍,置p = Pmax,pre2 = Pmax,pre4 = 50,pre5 = 10,pre6 = 29。

接着,按照上述步骤,依次检查图中的所有结点。总体思想是,按照拓扑序列检查到nodei 时,按顺序检查nodei 能到达的所有结点,如果发现从nodei -> nodej 不能到达,那么给nodei 放上加压器,重新按顺序检查、更新一遍 nodei 能到达的所有结点。检查时用最大边,更新时用最小边。

由于每次只是贪心地考虑当前结点,因此结果集可能会比标准结果集大一点,但经过测试,绝大多数情况下贪心的方法即是标准结果。

②dfs分支定界:
本题使用dfs进行递归也比较直观。使用一个结果集Vset[]表示当前图中加了放大器的点,初始时都是0。从源点1开始到结点n进行dfs,每搜索到一个新的结点,就对这个点进行左递归和右递归,左递归表示这个点不加压力放大器,右递归表示这个点要加压力放大器。递归完毕后保证每个点都被验证过0和1两种状态,有点类似穷举的方法,在全部可行解中找出最小的一个可行解。

在每次递归后,判断在当前加压情况下,从s出发是否能到达每一个点,如果能到达,说明这是一个可以考虑的分支,反之则说明这不是一个可考虑的分支。

如果这是一个可考虑的分支,那么要做对应的修改:如果得到的结果集比之前得到的小,即有更小的加压方式,那么把加压方式的数组和加压点个数进行更新。在所有修改操作后,筛选出结果集最小的一种情况。

反之,如果不是一个可考虑的分支,那么不做任何修改。判断该分支是否可行的方法也是利用了拓扑序列进行bfs遍历,可参考代码中相应部分。由于拓扑序列记录了每个点的入度,因此也可以顺便记录其出度,对于此方法,可以进行剪枝优化:把出度为0的点全部剪掉。

更新一个同学的做法:

③全排列:
每一个结点根据放置放大器和不放放大器可有1和0两种状态,求出所有结点状态的全排列,即列出所有可能的放置情况。然后,对于每一种放置情况,都对图进行一次遍历,看这种放置方法是否可行,对于所有的可行解维护一个最小值。

这种方法也是暴力枚举的思想,复杂度比较高,但确实写起来比较简单,遍历图也只需要一个函数就能完成。


文件下载地址

https://github.com/zj23333/CDDSA/

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值