数学建模运筹优化——规划问题Python版(动态规划、图论)

1、动态规划

        动态规划是运筹学的一个分支,通常用来解决多阶段决策过程最优化问题。动态规划的基本想法就是将原问题转换为一系列相互联系的子问题,然后通过逐层地推来求得最后的解。 

组成部分:

(以背包问题为例)

步骤一:确定状态

状态是指某一阶段问题的一个描述,它应该包含解决问题所需要的信息。在动态规划中,我们需要确定一个或多个状态变量,这些变量能够描述问题的每一个子问题的特征。

例如,在解决背包问题时,状态可以定义为dp[i][j],表示在前i件物品中选择,使得总重量不超过j的情况下,背包能够达到的最大价值。

步骤二:转移方程

转移方程(或称为状态转移方程)描述了状态之间如何转移,即如何从一个或多个已知状态推导出下一个状态。

继续以背包问题为例,转移方程可以是:

如果不选择第i件物品,则dp[i][j] = dp[i-1][j]

如果选择第i件物品,则dp[i][j] = dp[i-1][j-weights[i]] + values[i] 其中,weights数组存储物品的重量,values数组存储物品的价值。

转移方程通常是通过“最优子结构”来确定的,即问题的最优解包含了其子问题的最优解。

步骤三:初始条件和边界情况

在应用转移方程之前,需要定义初始状态和边界条件,这些是递推的起点。

对于背包问题,初始条件可能是:

dp[0][...] = 0,即没有物品时,背包价值为0。

dp[...][0] = 0,即背包容量为0时,背包价值也为0。

边界情况可能包括物品的重量大于当前背包容量时的处理等。

步骤四:计算顺序

计算顺序是指按照什么样的顺序来计算状态,确保在计算dp[i][j]时,它所依赖的状态已经被计算过。

在背包问题中,计算顺序通常是先物品后容量,即:

for i in range(1, n+1):
    for j in range(1, W+1):
        dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i]] + values[i] if j >= weights[i] else 0)

例题 

 

假设你是一名盗贼,你有一个背包,最多能承载W重量的物品。现在你面前有n件物品,每件物品有自己的重量weights[i]和价值values[i]。你希望在不超出背包承载重量的前提下,尽可能多地装入物品,使得背包内物品的总价值最大。

输入

n:物品数量

W:背包的最大承重

weights:长度为n的数组,表示每个物品的重量

values:长度为n的数组,表示每个物品的价值

输出

max_value:背包能装下的物品的最大价值

动态规划建模过程

  1. 确定状态:dp[i][j]表示考虑前i件物品,在容量为j的背包中能装入的最大价值。
  2. 转移方程:
    • 如果不选择第i件物品,则dp[i][j] = dp[i-1][j]
    • 如果选择第i件物品,则dp[i][j] = dp[i-1][j-weights[i]] + values[i]
  3. 初始条件和边界情况:
    • dp[0][...] = 0,没有物品时价值为0
    • dp[...][0] = 0,背包容量为0时价值为0
  4. 计算顺序:先物品后背包容量
def knapsack(n, W, weights, values):
    # 初始化dp数组,大小为(n+1) x (W+1),初始值为0
    dp = [[0 for _ in range(W+1)] for _ in range(n+1)]

    # 遍历物品
    for i in range(1, n+1):
        # 遍历背包容量
        for j in range(1, W+1):
            # 如果当前物品重量小于等于背包容量,可以选择该物品
            if weights[i-1] <= j:
                # 选择当前物品和不选择当前物品的最大值
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1])
            else:
                # 当前物品重量大于背包容量,不选择该物品
                dp[i][j] = dp[i-1][j]

    # 返回背包能装下的物品的最大价值
    return dp[n][W]

# 示例输入
n = 4  # 物品数量
W = 7  # 背包最大承重
weights = [1, 3, 4, 5]  # 每个物品的重量
values = [1, 4, 5, 7]   # 每个物品的价值

# 计算最大价值
max_value = knapsack(n, W, weights, values)
print("最大价值为:", max_value)

        这段代码中,knapsack函数实现了0-1背包问题的动态规划解法。我们首先创建了一个二维数组dp,用于存储每个子问题的解。然后,我们通过两层循环来填充这个数组,外层循环遍历物品,内层循环遍历背包容量。在填充数组的过程中,我们使用转移方程来确定每个状态的最大价值。最后,dp[n][W]就是背包能装下的物品的最大价值。 

7fc4576a9d6d46efa4c156fe3120628d.png2、图论         

         图论( Graph Theory )是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。 数学建模中的图,是根据实际问题简化而来的模型,对于平面上的n个点,把其中一些点用直线和曲线连接起来,不考虑所画点的位置和所画直线或曲线的长度,这样形成的关系称为“图”。

        图论中,每个节点称为顶点,连线称为边,边上标出的数字称 为边的权重(权重和线段画出来的长度无关)

147fa6d769f7471198f3fe19121d02c5.png

3fc2614407b141df86098b9640a2fb17.png

        图中的边如果是单向箭头,表示有方向,构成的图称为有向图,如示例中的城市路线,是有方向的 ;如果图里的边没有箭头,只有线,表示双向,构成的图称为无向图,比如通信光缆之类 ;权重不仅可以表示长度之类的,也可以表示时间、费用等概念。

60628eed91a84fe181801784ccea5fc1.png

 无向图代码实现

class Graph:
    def __init__(self):
        # 初始化一个空图,使用字典来存储图的边信息
        self.graph = {}

    def add_edge(self, u, v, w=1):
        # 添加边(u, v)到图中,权重为w
        # 如果节点u已经在图中,则添加v作为u的邻居
        if u in self.graph:
            self.graph[u].append((v, w))
        else:
            self.graph[u] = [(v, w)]
        
        # 由于是无向图,节点v也需要添加节点u作为邻居
        if v in self.graph:
            self.graph[v].append((u, w))
        else:
            self.graph[v] = [(u, w)]

    def get_neighbors(self, node):
        # 获取节点node的所有邻居节点及其权重
        return self.graph.get(node, [])

# 示例:表示城市之间的道路连接
graph = Graph()
graph.add_edge('A', 'B', 5)  # 城市A和城市B之间有道路,距离为5
graph.add_edge('B', 'C', 3)  # 城市B和城市C之间有道路,距离为3
graph.add_edge('A', 'C', 1)  # 城市A和城市C之间有道路,距离为1
graph.add_edge('C', 'D', 4)  # 城市C和城市D之间有道路,距离为4

# 打印节点A的邻居
print(graph.get_neighbors('A'))

53c05b5a09154c01917ad46be0d0237b.png

有向图代码实现

class Digraph:
    def __init__(self):
        # 初始化一个空有向图,使用字典来存储图的边信息
        self.graph = {}

    def add_edge(self, u, v, w=1):
        # 添加有向边u -> v到图中,权重为w
        if u in self.graph:
            self.graph[u].append((v, w))
        else:
            self.graph[u] = [(v, w)]

    def get_neighbors(self, node):
        # 获取节点node的所有出边邻居节点及其权重
        return self.graph.get(node, [])

# 示例:表示航班路线
digraph = Digraph()
digraph.add_edge('A', 'B', 100)  # 从城市A到城市B有航班,距离为100
digraph.add_edge('A', 'C', 200)  # 从城市A到城市C有航班,距离为200
digraph.add_edge('B', 'C', 50)   # 从城市B到城市C有航班,距离为50
digraph.add_edge('C', 'D', 75)   # 从城市C到城市D有航班,距离为75

# 打印从节点A出发的航班
print(digraph.get_neighbors('A'))

d6a217610f754091b1b9c871a5851533.png

        通过图论等方法可以后续解决求解最短路径、路径规划等问题。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值