1.什么是动态规划
动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著Dynamic Programming,这是该领域的第一本著作。
2.动态规划的适用范围
动态规划通常应用于最优化问题。此类问题可能有很多种可行解,但我们需要找出一个符合我们条件的最优解。这类问题通常可通过分解成成子问题而求解。首先动态规划是通过组合子问题的解而解决整个问题的。这在某种程度上和分治有点类似,但分治法是将问题分成独立的子问题,而动态规划适用于子问题不是独立的情况,也就是说各个子问题包含着相同的子子问题。动态规划算法对各个子子问题只求解一次,将结果保存下来,下次遇到的时候即可直接适用而无需重复计算。
3.动态规划的特征
(1)最优子结构
如果问题的一个解中包含了子问题的最优解,则该问题具有最优子结构。
(2)重叠子问题
第二个特征是当分解原问题时我们所得到的子问题必须是相同的,而不是在产生新的子问题,比如分治法在应用的时候不断产生新的子问题。动态规划算法总是充分利用了子问题是相同的这一特征,每个子问题只求解一次,即第一次遇到的时候进行求解,将其保存在一张表中,以后在使用时直接查询获取。
4.动态规划的两个例子
(1)最长公共子序列
问题描述:
一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
首先我们定义X(i)为字符串X的的前i个字符组成的字串,Y(j)为字符串Y的前j个字符组成的字串,很明显Y(0)为空串。Z(k)为X(i)和Y(j)的最长公共子序列。那么如果X(i)的最后一个字符x(i)等于Y(j)的最后一个字符y(j)的话,那么z(k-1)也必然是X(i-1)和Y(j-1)的最长公共子序列。通过反证法很容易得到这一点。这就体现了最优子结构,问题的最优解包含了子问题的最优解。同时我们也看到有很多子子问题是相同的,需要我们重复解决。 令c(i)(j)为X(i)和Y(j)的公共子序列长度。 (a)如果X(i)=Y(j),那么c(i)(j)=c(i-1)(j-1); (b)如果X(i)!=Y(j),那么c(i)(j)=max(c(i-1)(j),c(i)(j-1)); 由上面的状态转移方程即可得出,原问题的解即为 c(len1)(len2),len1、len2分别为字符串的长度。
(2)编辑距离
问题描述:
编辑距离(Edit Distance),又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个
字符
替换成另一个字符,插入一个字符,删除一个字符。
例如将kitten一字转成sitting:
sitten (k→s)
sittin (e→i)
sitting (→g)
同样的,我们定义c[i][j]为字符串X(i)向字符串Y(j)转换的操作步骤数。
类似的,我们考虑如果
(a). X(i)的最后一个字符x(i)==Y(j)的最后一个字符y(j)
那么c[i][j]=c[i-1][j-1];
因为由定义我们知道c[i-1][j-1]是字符串X(i-1)向字符串Y(j-1)的编辑次数,最后一个字符相等的话即x(i)==Y(j),那么最后一个字符无需编辑即完成了字符串X(i)向Y(j)的编辑。
(b).X(i)的最后一个字符!=Y(j)的最后一个字符y(j)
这里又要分三种情况
(1)如果最后一个字符x(i)需要替换成y(j)话,那么 c[i][j]=c[i-1][j-1]+1;
(2)如果最后一个字符x(i)需要删除的话,那么 c[i][j]=c[i-1][j]+1;
因为由定义可知,c[i-1][j]是字符串X(i-1)转换成Y(j)所需的操作次数,那么也就是说在字符串X(i-1)已经成功转换成了Y(j),那么显然最后一个字符x(i)需要删除
(3)如果需要在最后一个字符x(i)后面添加一个字符即y(j)的话,那么c[i][j]=c[i][j-1]+1;
因为由定义知道,c[i][j-1]是字符串X(i)转换成Y(j-1)所需的操作次数,想要转换成Y(j)的话,只能增加一步添加字符的操作,即插入字符y(j)
综上所述,c[i][j]=min(c[i-1][j-1],c[i-1][j],c[i][j-1])+1;
csdn编程挑战模块有道简易版的编辑距离问题 http://student.csdn.net/mcs/programming_challenges
描述如下 :
传统的编辑距离里面有三种操作,即增、删、改,我们现在要讨论的编辑距离只允许两种操作,即增加一个字符、删除一个字符。我们求两个字符串的这种编辑距离,即把一个字符串变成另外一个字符串的最少操作次数。 输入格式: 多组数据,每组数据两行,每行一个字符串。每个字符串长度不超过1000,只有大写英文字母组成。 输出格式: 每组数据输出一行包含一个整数,表示需要最少操作的次数。
通过代码如下:
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int min(int a,int b)
{
return a<b?a:b;
}
int c[1030][1030];
char stra[10130],strb[1030];
int main(int argc, const char * argv[])
{
while(scanf("%s",stra)!=EOF)
{
scanf("%s",strb);
int len1=strlen(stra);
int len2=strlen(strb);
for (int i=0; i<=len1; i++)
{
c[0][i]=i;
}
for (int j=0; j<=len2; j++)
{
c[j][0]=j;
}
for (int i=1; i<=len2;i++ )
for (int j=1; j<=len1; j++)
{
if (strb[i-1]==stra[j-1])
{
c[i][j]=c[i-1][j-1];
}
else
{
c[i][j]=min(c[i-1][j], c[i][j-1])+1;
}
}
printf("%d\n",c[len2][len1]);
}
return 0;
}