动态规划(下):如何求得状态转移方程并进行编程实现?

动态规划(下):如何求得状态转移方程并进行编程实现?

状态转移方程和编程实现

这里面求最小值的 min 函数里有三个参数,分别对应我们上节讲的三种情况的编辑距离,分别是:替换、插入和删除字符。在表格的右下角标出了两个字符串的编辑距离 1。

我们假设字符数组 A[]和 B[]分别表示字符串 A 和 B,A[i]表示字符串 A 中第 i 个位置的字符,B[i]表示字符串 B 中第 i 个位置的字符。二维数组 d[,]表示刚刚用于推导的二维表格,而 d[i,j]表示这张表格中第 i 行、第 j 列求得的最终编辑距离。函数 r(i, j) 表示替换时产生的编辑距离。如果 A[i]和 B[j]相同,函数的返回值为 0,否则返回值为 1。

  • 如果 i 为 0,且 j 也为 0,那么 d[i, j]为 0。
  • 如果 i 为 0,且 j 大于 0,那么 d[i, j]为 j。
  • 如果 i 大于 0,且 j 为 0,那么 d[i, j]为 i。
  • 如果 i 大于 0,且 j 大于 0,那么 d[i, j]=min(d[i-1, j] + 1, d[i, j-1] + 1, d[i-1, j-1] + r(i, j))。

这里面最关键的一步是 d[i, j]=min(d[i-1, j] + 1, d[i, j-1] + 1, d[i-1, j-1] + r(i, j))。这个表达式表示的是动态规划中从上一个状态到下一个状态之间可能存在的一些变化,以及基于这些变化的最终决策结果。我们把这样的表达式称为状态转移方程。

有了状态转移方程,我们就可以很清晰地用数学的方式,来描述状态转移及其对应的决策过程,而且,有了状态转移方程,具体的编码其实就很容易了。

如果我们使用动态规划法来实现编辑距离的测算,那就能确保查询推荐的效率和效果。不过,基于编辑距离的算法也有局限性,它只适用于拉丁语系的相似度衡量,所以通常只用于英文或者拼音相关的查询。如果是在中文这种亚洲语系中,差一个汉字(或字符)语义就会差很远,所以并不适合使用基于编辑距离的算法。

实战演练:钱币组合的新问题

给定总金额和可能的钱币面额,能否找出钱币数量最少的奖赏方式?

假设这里我们有三种面额的钱币,2 元、3 元和 7 元。为了凑满 100 元的总金额,我们有三种选择。

第一种,总和 98 元的钱币,加上 1 枚 2 元的钱币。如果凑到 98 元的最少币数是 x_{1},那么增加一枚 2 元后就是 (x_{1} + 1) 枚。

第二种,总和 97 元的钱币,加上 1 枚 3 元的钱币。如果凑到 97 元的最少币数是 x_{2},那么增加一枚 3 元后就是 (x_{2} + 1) 枚。

第三种,总和 93 元的钱币,加上 1 枚 7 元的钱币。如果凑到 93 元的最少币数是 x_{3},那么增加一枚 7 元后就是 (x_{3} + 1) 枚。

比较一下以上三种情况的钱币总数,取最小的那个就是总额为 100 元时,最小的钱币数。换句话说,由于奖赏的总金额是固定的,所以最后选择的那枚钱币的面额,将决定到上一步为止的金额,同时也决定了上一步为止钱币的最少数量。根据这个,我们可以得出如下状态转移方程:

其中,c[i]表示总额为 i 的时候,所需要的最少钱币数,其中 j=1,2,3,…,n,表示 n 种面额的钱币,value[j]表示第 j 种钱币的面额。c[i - values(j)]表示选择第 j 种钱币的时候,上一步为止最少的钱币数。需要注意的是,i - value(j) 需要大于等于 0,而且 c[0] = 0。

表格每一行表示奖赏的总额,前 3 列表示 3 种钱币的面额,最后一列记录最少的钱币数量。表中的“/”表示不可能,或者说无解。

这张状态转移表同样可以帮助你来理解状态转移方程的正确性。一旦状态转移方程确定了,要编写代码来实现就不难了。

总结

首先,如果一个问题有很多种可能,看上去需要使用排列或组合的思想,但是最终求的只是某种最优解(例如最小值、最大值、最短子串、最长子串等等),那么你不妨试试是否可以使用动态规划。

其次,状态转移方程是个关键。你可以用状态转移表来帮助自己理解整个过程。如果能找到准确的转移方程,那么离最终的代码实现就不远了。当然,最好的方式,还是结合工作中的项目,不断地实践,尝试,然后总结。

原文地址:https://www.cnblogs.com/liugangjiayou/p/12689590.html

时间: 2024-10-10 05:41:16

动态规划(下):如何求得状态转移方程并进行编程实现?的相关文章

【转载】 HDU 动态规划46题【只提供思路与状态转移方程】

1.Robberies 连接 :http://acm.hdu.edu.cn/showproblem.php?pid=2955 背包;第一次做的时候把概率当做背包(放大100000倍化为整数):在此范围内最多能抢多少钱  最脑残的是把总的概率以为是抢N家银行的概率之和- 把状态转移方程写成了f[j]=max{f[j],f[j-q[i].v]+q[i].money}(f[j]表示在概率j之下能抢的大洋); 正确的方程是:f[j]=max(f[j],f[j-q[i].money]*q[i].v)  其

DP问题各种模型的状态转移方程 (转)

1(最长公共子串(注意和最长公共子序列区别)) 两个字符串str1和str2,长度分别为(l1,l2) dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度,由于下面涉及到i-1和j-1,那么这个时候我们一般从i=1和j=1开始到i<=len1, j<=len2. if(str[i-1]=str[j-1]) dp[i][j]=dp[i-1][j-1]+1; if(str[i-1]!=str[j-1]) dp[i][j]=0; 0 ;              

Mark一下, dp状态转移方程写对,但是写代码都错,poj 1651 poj 1179

dp题: 1.写状态转移方程; 2.考虑初始化边界,有意义的赋定值,还没计算的赋边界值: 3.怎么写代码自底向上计算最优值 今天做了几个基础dp,全部是dp方程写对但是初始化以及计算写错 先是poj 1651 其实就是个赤裸裸的矩阵连乘,dp方程很容易写出 dp[i][j]=min(dp[i][k]+dp[k+1][j]+r[i]*c[k]*c[j],dp[i][j]); 先贴两个个二逼的代码,mark下自己多么的二逼: 二逼一:在计算的时候使用了还没有算出来的值,模拟下就知道第一重循环里算dp

[转]DP问题各种模型的状态转移方程

1(最长公共子串(注意和最长公共子序列区别)) 两个字符串str1和str2,长度分别为(l1,l2) dp[i][j]表示以两个字符串分别以第i和第j个字符结尾所能达到的公共子序列的长度,由于下面涉及到i-1和j-1,那么这个时候我们一般从i=1和j=1开始到i<=len1, j<=len2. if(str[i-1]=str[j-1]) dp[i][j]=dp[i-1][j-1]+1; if(str[i-1]!=str[j-1]) dp[i][j]=0; 0 ;              

动态规划(DP),压缩状态,插入字符构成回文字符串

题目链接:http://poj.org/problem?id=1159 解题报告: 1.LCS的状态转移方程为 if(str[i-1]==str[j-1]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 2.由于开不了dp[5005][5005],于是考虑到压缩状态 这里采用滚动数组方式,LCS的状态转移方程可以改写为 if(str1[i-1]==str2[j-1]) { dp[i%2][j]=dp[(i-1

Bootstrap系列 -- 28. 下拉菜单状态

下拉菜单项的默认的状态(不用设置)有悬浮状态(:hover)和焦点状态(:focus). 下拉菜单项除了上面两种状态,还有当前状态(.active)和禁用状态(.disabled).这两种状态使用方法只需要在对应的菜单项上添加对应的类名 <div class="dropdown"> <button class="btn btn-default dropdown-toggle" type="button" id="dro

windows下监控vpn状态及中断后自启动

windows服务器vpn远程到内网与内网主机通信,因vpn客户端或其它原因,vpn链接会不定期中断,导致与内网通信中断,中断时不清楚vpn进程是否结束,解决思路如下,前提是客户端启动后会自动拨号链接    判断vpn进程是否存在-->判断与vpn服务器是否为链接状态,若否则启动vpn程序,vpn.bat脚本内容如下    @echo off    关闭回显    c:    切换到vpn客户端所在分区    cd C:\Program Files (x86)\vpn\SSL\vpnClient

CentOS下查看网络状态

查看网络状态:lsof -Pnl +M -i4 显示ipv4服务及监听端情况netstat -anp 所有监听端口及对应的进程netstat -tlnp 功能同上 网络基本命令 (1)network service的制御网络接口配置信息改动后,网络服务必须从新启动,来激活网络新配置的使得配置生效,这部分操作和从新启动系统时时一样的作用.制御(控制)是/etc/init.d/network这个文件,可以用这个文件后面加上下面的参数来操作网络服务.例如: [[email protected] ~]#

linux下U盘状态检测

Linux的文件系统是异步的,也就是说写一个文件不是立刻保存到介质(硬盘,U盘等)中,而是存到缓冲区内,等积累到一定程度再一起保存到介质中.如果没有umount就非法拔出U盘,程序是不知道的,fopen,fwrite等函数都依然返回正确,知道操作系统要把写介质的时候,才会提示I/O错误.可是很多数据都会因为这个不及时的错误报告而丢失.    事实上,USB驱动程序在U盘插入和拔出时,都对系统配置文件做了修改.    例如U盘驱动程序会在插入或拔出时往 /proc/scsi/usb-storage