Algorithm 动态规划与矩阵的使用
我总是对动态规划如何使用矩阵来解决问题感到困惑。我大致了解到,矩阵用于存储以前子问题的结果,以便在以后计算更大的问题时使用 但是,如何确定矩阵的维数,我们如何知道矩阵的每一行/每一列应该代表什么值?也就是说,是否有一个构建矩阵的通用过程 例如,如果我们有兴趣使用价值为c1、c2、.cn的硬币来更改货币的数量,那么矩阵的维数应该是多少,每列/每行应该代表什么Algorithm 动态规划与矩阵的使用,algorithm,dynamic,Algorithm,Dynamic,我总是对动态规划如何使用矩阵来解决问题感到困惑。我大致了解到,矩阵用于存储以前子问题的结果,以便在以后计算更大的问题时使用 但是,如何确定矩阵的维数,我们如何知道矩阵的每一行/每一列应该代表什么值?也就是说,是否有一个构建矩阵的通用过程 例如,如果我们有兴趣使用价值为c1、c2、.cn的硬币来更改货币的数量,那么矩阵的维数应该是多少,每列/每行应该代表什么 任何方向指引都会有所帮助。谢谢大家! DP解决方案使用的数组几乎总是基于问题状态空间的维度,即每个参数的有效值 比如说 fib[i+2] =
任何方向指引都会有所帮助。谢谢大家! DP解决方案使用的数组几乎总是基于问题状态空间的维度,即每个参数的有效值 比如说
fib[i+2] = fib[i+1] + fib[i]
与
def fib(i):
return fib(i-1)+fib(i-2]
通过在递归函数中实现记忆,可以使这一点更加明显
def fib(i):
if( memo[i] == null )
memo[i] = fib(i-1)+fib(i-2)
return memo[i]
如果递归函数有K个参数,则可能需要K维矩阵。本章对此进行了很好的解释:
在第178页,它给出了一些方法来识别允许您应用动态规划的子问题。当一个问题同时表现出两个方面时,它就符合动态规划的条件 其次,动态规划有两种变体:
- 制表法还是自下而上法
- 记忆或自上而下的方法(不是记忆!)
c={1,2,10}
取一套空硬币,从c
中每行添加一枚硬币。下一行每增加一枚硬币。这些列表示子问题。对于n=1:10
中的i
,第i
列表示使用该行硬币可以构造i
的方式总数:
--------------------------------------------------------
|0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
--------------------------------------------------------
|{} | | | | | | | | | | | |
--------------------------------------------------------
|{1} | | X | | | | | | | | | |
--------------------------------------------------------
|{1, 2} | | | | | | | | | | | |
--------------------------------------------------------
|{1, 2, 10}| | | | Y | | | | | | | Z |
--------------------------------------------------------
在此表中,X
表示使用硬币{1}
构造金额1的方式数,Y
表示使用硬币{1,2,10}
表示金额3的方式数,Z
表示使用硬币{1,2,10}表示金额10的方式数
单元格是如何填充的
最初,以0
为首的整个第一列都用1
s填充,因为无论您拥有多少硬币,对于0的金额,您只有一种方法可以进行更改,即不进行更改。
第一行的剩余部分是空子集{}
,其中填充了0
s,因为没有硬币时,您无法更改任何正值。
现在矩阵如下所示:
--------------------------------------------------------
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
--------------------------------------------------------
|{} |1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
--------------------------------------------------------
|{1} |1 | X | | | | | | | | | |
--------------------------------------------------------
|{1, 2} |1 | | | | | | | | | | |
--------------------------------------------------------
|{1, 2, 10}|1 | | | Y | | | | | | | Z |
--------------------------------------------------------
现在,我们如何填写X
?您有两种选择,要么在这个新的超级集合中使用1
硬币,要么不使用它。如果您没有使用硬币,方法与上面的行相同,即0
。但是由于1
可以用来更改金额1
,我们使用该硬币,从1
金额中减去1
,剩下0
。现在在同一行中查找,0
的方式,即X
前面的列,即1
。因此,将其添加到顶行的金额中,总数为1
。因此,将此单元格填充为1
但是,如何确定矩阵的维数,我们如何知道矩阵的每一行/每一列应该代表什么值?也就是说,是否有一个构建矩阵的通用过程
您需要找到表示子问题所需的递归关系和状态(参数数量)。DP的整体思想是避免子问题的重新计算。您只在第一次需要时计算一次子问题,将其存储在内存中,并在需要时引用存储的值。因此,如果希望稍后引用子问题的存储结果,则需要具有唯一标识子问题的键。子问题的状态通常是该键的良好选择。如果子问题有3个参数x
,y
,z
,那么元组(x的值,y的值,z的值)
就是将子问题的结果存储在哈希表中的好键。如果这些值是正整数,则可以使用矩阵,即多维数组,而不是哈希表。让我们发展一下寻找的想法
sort(0, n) = merge(sort(0, n/2), sort(n/2, n))
F(n) = min(F(n-p), F(n-q), F(n-r)) + 1
# Base conditions
F(0) = 0
F(n) = infinity if n < 0
F(n, only p allowed) = F(n-p, only p allowed)
## Base condition
F(0) = 1 # There is only one way to select 0 coins which is not selecting any coinss
F(n, p and q allowed) = F(n-q, p and q allowed) + F(n, only p allowed)
F(n, p q and r allowed) = F(n-r, p q and r allowed) + F(n, p and q allowed)
# F(n, i) = with denominations[i] + without denominations[i]
F(n, i) = F(n - denominations[i], i) + F(n, i-1)
## Base conditions
F(n, i) = 1 if n == 0
F(n, i) = 0 if n < 0 or i < 0