常见动态规划的解决思路

栏目: 编程工具 · 发布时间: 6年前

内容简介:给定两个字串x和y,将x变成y所需要改变的字符的个数是多少?期间可以的操作是:字串可以是非连续的。它等效于找两个字符串的最大公共字串,比如 HIEROGLYPHOLOGY 和 MICHAELANGELO 最大公共字串为 HELLO数量为:

给定两个字串x和y,将x变成y所需要改变的字符的个数是多少?期间可以的操作是:

  • 插入字符c 耗时O(1)
  • 删除字符c 耗时O(1)
  • 将c更新为c' 如果C和C'是相同的,耗时O(0),其它不考虑

字串可以是非连续的。它等效于找两个字符串的最大公共字串,比如 HIEROGLYPHOLOGY 和 MICHAELANGELO 最大公共字串为 HELLO

动态规划思路

  1. 子问题:X的子串X[i:]和Y的子串Y[j:]

数量为:

DP(i,j)=min(cost of replace X[i] to Y[j]+ DP(i+1,j+1),cost of delete X[i]+DP(i+1,j),cost of insert Y[j]+DP(i,j+1) )

子问题的耗时:O(1)

  1. 拓扑排序:三个方向由右下角向左下角开始执行
    常见动态规划的解决思路
for i in 0,...,|x|:
    for j in 0,...,|y|:
复制代码

原始的问题:DP(0,0),总的时间为:O(1)*O(|x||y|)

矩阵相乘在哪个部分加括号运算才能使得运算最优

假设有如下形式的矩阵做乘法

常见动态规划的解决思路

如果直接按照顺序来计算,先乘A.B,得到的结果再乘C

常见动态规划的解决思路

如果优先运算 B.C ,结果再乘A

常见动态规划的解决思路

可以看到第二种方式消耗的时间会更少。

扩展到假设有n个矩阵相乘

那么如何实现通过加括号的方式来最优执行效率呢?

思路

最终总会是两个矩阵相乘,那这两个矩阵是怎么计算得到的?假设有一次区分,那么它是( ).( )这种样子,左边括号中也会继续按照这种方式划分,同理右边也需要,当左右两边都需要类似计算的时候,这种时候通常就是取substring

动态规划解决思路

  1. 找到子问题:计算

子问题的个数为 。每次选择相当于1分为2

  1. 猜测:最后该执行那个部分的计算?

执行应该是( ).( )这种样子 有的选择的数量:O(n)。去尝试所有的可能

  1. 循环:

需要分别计算左边括号的部分和右边括号的部分,以及最后计算出来的左右两个部分的乘积

DP(i,i+1)=0

子问题的时间:O(n)。D(i,k)的计算忽略不计,只有k的选择存在循环

  1. 拓扑执行顺序:增加子问题的大小

总时间消耗:

  1. 原始的问题为:DP(0,n)

如何使得词在段落中的位置分配合理,使得更美观

给定一个词的集合words,使用badness(i,j)表示使用的单词是words[i,j]

目标就是使得分出来的词展示在各个行,使得 最小

不好看的展示如下

blah blah blah  
b    l    a   h  
verylongworld
复制代码

希望它能展示成

blah    blah 
blah    blah  
verylongworld
复制代码

暴力解决方案

一个个的去尝试所有单词的划分,也就是说,去判断任意一个词,是否应该在它的位置换行,如果有n个单词的话,总共需要尝试的次数是

动态规划

按照标准的动态规划步骤来进行:

  1. 找到子问题:集合的后缀 words[i:]

假设找到了第一行的分隔点,那么接下来需要考虑的是第二行又该在哪儿开始换行呢?依次继续往下去查找,所以需要思考的子问题就是去掉第一行的词之后,剩下的那些单词

子问题的数量:n。只有n个单词,后缀的次数也就是这些

  1. 猜测:第二行从哪儿开始?

猜测的选择的数量:<=n-i=O(n)。每次选完了第一行,只需要在剩下的单词里面选

  1. 循环: DP[i]=min(badness(i,j)+DP[j] for j in range(i+1,n+1))

定义问题为求DP(i)的最小值。当i=n的时候,是没有单词剩下的,花销是0。

假设第一次在第i个位置开始换行,第一行的计算发方式为 badness(i,j),剩下的需要解决的问题部分是从i+1开始的单词,也就是剩下部分的花销假设从j开始,它可能取得剩下部分的任意值,每个j的取值所需要的花销就是D(j),那么这两部分相加最小时,也就是最优的划分方式

循环部分耗时(每个子问题耗时):O(n)。j总共有n种选择,加法部分是常量

  1. 拓扑排序:i=n,n-1,...,0

需要先从最末端开始计算,再一层层的网上去累加 时间为:问题的数量*每个问题的花销=

  1. 检验原始问题是否解决:即DP(0)是否解决

使用一个指针parent来表明j中的最小值是那个,那么沿着parent指针,0->parent[0]->parent[parent[0]]一直到最后,即可得到最佳的划分方式

吉他放手指的问题

有一个吉他,在弹的时候,可以用任何一个手指来谈,那么如果给予一系列的音符notes,每个音符都需要用手(手指头取值 1,..,F)处理,每个手指去某个音符弹后需要移到另一个音符用一个手指去弹,假设描述这种移动使用d(p,f,q,g)表示花销,那如何去使得花销最小?

d(p,f,q,g):表示手指f移动到g,弹的音符从p到q

一般思考是:

  1. 子问题:notes[i:],即后面的音符怎么去弹
  2. 猜测:该用那个手指头来谈音符i
  3. 循环:可以选择任意一个手指头去弹新的音符,但是当从旧的音符切换到新的音符去弹的时候,无法知道该切换到那个手指: DP(i)=min(DP(i+1)+d(i,f,i+1,?) for f in 1,..F)
    所以子问题"记住"的过少,需要增加考虑的情况。

正确思路为:

  1. 子问题:notes[i:],即后面的音符怎么去弹,同时该那个手指f去弹notes[i:]
  2. 猜测:使用手指g来谈notes[i+1]
  3. 循环: DP(i,f)=min(DP(i+1,g)+d(i,f,i+1,g) for g in 1,..F)

i表示note[i]的音符

  1. 拓扑排序
for i in reversed(range(n)): //所有的音符
    for f in 1...F: //每个手指头都有可能来弹音符
    
复制代码

最终去解决原始问题,DP(0,f),但是需要指定f,所以使用 min(DP(0,f) for f in 1,...,F),即初始的时候应该选择那一个手指去谈第一个音符

这种场景即是要考虑更多的情况,来达到最优解


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Sovereign Individual

The Sovereign Individual

James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00

Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码