一、动态规划的原理
动态规划是一种解决多阶段决策问题的优化方法,把多阶段问题转换为一个个单阶段问题,利用各个阶段之间的关系求解
二、动态规划法的性质
最优解原理:问题的最优解包含的子问题也是最优的。
无后效性:该问题状态一旦确定,就不受这个状态以后决策的影响。
有重叠子问题:子问题之间不独立。子问题的解在下一段决策时可能会被多次使用到,就像计算Fib(5)时可能用到Fib(3),储存到数组中。这也正是动态规划相较于其他算法的优势。
三、动态规划法的一般步骤:
-
分析最优解的性质和结构特性
-
递归地定义最优值
-
以自底向上(递推)的方式求解最优值
-
根据最优值的信息构造最优解
四、动态规划的例子(8.8或8.5为填空题最后一题)
8.2求解整数拆分问题:
备忘录方法是动态规划方法的变形,在我的理解中就是递归和DP数组的结合,因为采用了递归所以是自顶向下,而动态规划只利用了DP数组,从i,j=0开始计算,所以是自底向上
动态规划自底向上:
#include <stdio.h>
#define MAXN 500
int dp[MAXN][MAXN]; //动态规划数组
void Split(int n,int k) //求解算法
{
for (int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
{
if (i==1 || j==1)
dp[i][j]=1;
else if (i<j)
dp[i][j]=dp[i][i];
else if (i==j)
dp[i][j]=dp[i][j-1]+1;
else
dp[i][j]=dp[i][j-1]+dp[i-j][j];
}
}
void main()
{
int n=5,k=5;
Split(n,k);
printf("(%d,%d)=%d\n",n,k,dp[n][k]); //输出:7
}
备忘录法自顶向下
//求解整数拆分问题的算法
#include <stdio.h>
#include <string.h>
#define MAXN 500
int dp[MAXN][MAXN];
int dpf(int n,int k)
{
if (dp[n][k]!=0) return dp[n][k];
if (n==1 || k==1)
{
dp[n][k]=1;
return dp[n][k];
}
else if (n<k)
{
dp[n][k]=dpf(n,n);
return dp[n][k];
}
else if (n==k)
{
dp[n][k]=dpf(n,k-1)+1;
return dp[n][k];
}
else
{
dp[n][k]=dpf(n,k-1)+dpf(n-k,k);
return dp[n][k];
}
}
void main()
{
int n=5,k=5;
memset(dp,0,sizeof(dp)); //初始化为0
printf("dpf(%d,%d)=%d\n",n,k,dpf(n,k)); //输出:7
}
8.5求解最长公共子序列问题(重点,填表,根据表格找出最长公共子序列)
8.8求解0/1背包问题(填表+追溯最优解)
状态转移方程
例题
追溯最优解很简单,首先从右下角找起,右下角和上一行同列比较,如果相同则代表没有选i行的物品,最优解向量x[i]=0,再对i-1行同样操作。如果不一样,就代表选了,则x[i]=1,用剩余背包容量r-w[i],并找到r-w[i]列,对该列进行相同的操作。
复习题:算法分析与设计(李春葆)第八章1~8
先这样,等复习到后面再来复习一遍填表和追溯最优解。