Fancy DSA Fancy DSA
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

想的更多,也要想的更远
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • Roadmap 路线
  • 算法简介
  • Sort 排序

  • Search 搜索

  • Recursive 递归

  • Graph 图

  • Tree 树

  • Math 数学

  • Hash 哈希

  • String 字符串

  • BitManipulation 位操纵

  • Backtracking 回溯

  • DynamicProgramming 动态规划

    • ClimbingStairs [爬楼梯]
    • CoinChange [钱币兑换]
    • EditDistance [编辑距离]
    • FibonacciNumber [斐波那契数]
    • FindMonthCalendar [月历]
    • KadaneAlgo [最大连续子数组和之Kadane算法]
    • LevenshteinDistance [莱文斯坦距离]
    • LongestCommonSubsequence [最长公共子序列]
    • LongestIncreasingSubsequence [最长递增子序列]
    • LongestPalindromicSubsequence [最长回文子序列]
    • LongestValidParentheses [最长合法括号]
    • MaxNonAdjacentSum [最大非连接子集和]
    • MaxProductOfThree [最大三数积]
    • MinimumCostPath [最小代价路径]
    • NumberOfSubsetEqualToGivenSum [等和子集]
    • RodCutting [棒材切割问题]
      • 介绍
      • 递归法
      • 动态规划法
      • 实现
      • 参考
    • Shuf [随机样本]
    • SieveOfEratosthenes [埃拉托斯特尼筛法]
    • SlidingWindow [滑窗]
    • SudokuSolver [数独]
    • TrappingRainWater [接住雨水]
    • TribonacciNumber [翠波那契数]
    • ZeroOneKnapsack [零一背包]
  • Cache 缓存

  • Array 数组

  • Ciphers 密码学

  • Conversions 转换

  • ProjectEuler 欧拉计划

  • 其他

  • 算法
  • DynamicProgramming 动态规划
jonsam
2022-09-26
目录

RodCutting [棒材切割问题]

# 介绍

给你一根长度为 n 的杆子和一个与所有长度小于 n 的价格数组。通过切割杆子和出售这些杆子,找出可能的最大利润。

# 递归法

我们得到一个数组 price [],其中长度的杆 i 有价值 price [i-1]. 这个想法很简单 —— 一根一根地分割给定的长度棒 n 分为两部分: i 和 n-i。重复分割 n-1 长度的棒,但不要分割长度为 i 的棒。最后,取所有值中的最大值。这产生以下递归的关系: rodcut(n) = max { price[i – 1] + rodCut(n – i) } where 1 <= i <= n 。

def rodCut(price, n):
    if n == 0:
        return 0
 
    maxValue = -sys.maxsize
    # 将给定的长度为`n`的杆分成两部分长度
    # (1, n-1), (2, n-2), (3, n-3), … ,(n-1, 1), (n, 0) 取最大值
    for i in range(1, n + 1):
        # 长度为 `i` 的杆的成本为 `price[i-1]`
        cost = price[i - 1] + rodCut(price, n - i)
        if cost > maxValue:
            maxValue = cost
 
    return maxValue
1
2
3
4
5
6
7
8
9
10
11
12
13
14

上述解决方案的时间复杂度为 O (nn) 并占用调用堆栈中的空间,其中 n 是杆长。

我们已经看到问题可以分解为更小的子问题,这些子问题可以进一步分解为更小的子问题,依此类推。所以,问题有最优子结构。让我们考虑一个长度为 4 的杆的递归树。

image

正如我们所看到的,相同的子问题 (以相同的颜色突出显示) 被重复计算。所以,重叠子问题特征也表现出来了。我们知道具有最优子结构和重叠子问题的问题可以通过动态规划来解决,其中子问题的解决方案是缓存化而不必重复计算。

# 动态规划法

动态规划和分治法的区别:

  • 分治法是将问题划分成一些独立的子问题,递归求解各个子问题,然后合并子问题的解而得到原问题的解
  • 动态规划使用于子问题不独立的情况,也就是各个子问题包含公共的部分。若采用分治法,会有重复的求解公共部分,而动态规划算法对每个子问题只求解一次,然后将结果保存在一张表中,从而避免重复的运算。

动态规划具有如下特征:

  • 最优子结构
  • 重叠子问题

动态规划算法设计可以分为如下步骤:

  • 描述最优解的结构
  • 递归定义最优解的值
  • 按自底向上的方式计算最优解的值
  • 由计算出的结果构造一个最优解

我们将以自下而上的方式解决这个问题。在自下而上的方法中,我们首先解决较小的子问题,然后从中解决较大的子问题。以下自下而上的方法计算 T [i],它存储从长度为 i 的杆获得的最大利润。

def rodCut(price, n):
    T = [0] * (n + 1)
    for i in range(1, n + 1):
        for j in range(1, i + 1):
            T[i] = max(T[i], price[j - 1] + T[i - j])
    return T[n]
1
2
3
4
5
6

上述自下而上解的时间复杂度为 O (n^2) 并要求 O (n) 额外的空间,其中 n 是杆长。

# 实现

# JavaScript

/*
  * You are given a rod of 'n' length and an array of prices associated with all the lengths less than 'n'.
  * Find the maximum profit possible by cutting the rod and selling the pieces.
*/

function rodCut (prices, n) {
  const memo = new Array(n + 1)
  memo[0] = 0

  for (let i = 1; i <= n; i++) {
    let maxVal = Number.MIN_VALUE
    for (let j = 0; j < i; j++) maxVal = Math.max(maxVal, prices[j] + memo[i - j - 1])
    memo[i] = maxVal
  }

  return memo[n]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 参考

  • Cutting a Rod | DP-13 - GeeksforGeeks (opens new window)
编辑 (opens new window)
上次更新: 2022/10/27, 20:28:55
NumberOfSubsetEqualToGivenSum [等和子集]
Shuf [随机样本]

← NumberOfSubsetEqualToGivenSum [等和子集] Shuf [随机样本]→

最近更新
01
0-20题解
10-31
02
本章导读
10-31
03
算法与转换:Part1
10-28
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy DSA | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式