动态规划的简单总结

本文是对动态规划的简单总结,主要分为两类问题:求最优解(如背包问题)和求解的个数(如整数拆分问题)。通过设立二维动态规划数组,利用状态转移方程求解,例如背包问题的关键状态转移方程是dp[i][r]=MAX(dp[i-1][r], dp[i-1][r-w[i]]+v[i])。动态规划的核心在于找到正确状态转移方程,有时可使用滚动数组减少空间复杂度。" 106696152,7169349,Python TCP网络编程实战,"['Python', '网络编程', 'socket']
首先从斐波那契数列来看动态规划,递推公式为:f(n+2)=f(n+1)+f(n)。
若普通求解f(n),则需要一直往下递推,直到f(1)和f(2)为止,但用动态规划,使用dp数组储存每个阶段的f(n),则每次f的求解都不需要循环往下计算,只需调用dp数组中n-1与n-2阶段的f值即可。
    这样大大节约运行时间,动态规划也就是做这么件事。

动态规划的使用场景:各阶段的最优解有一定的相关型,某一阶段的最优解可以根据前一阶段的最优解再计算的来。
dp数组是最优解数组,任意数据是当前情况下的最优解(动态规划的核心
如何确定dp数组(如何计算出dp数组)是解题的关键。(找出规律)

动态规划的题型我大概将其分为两类:
一:求最优解问题。(背包问题)
二:求解的个数问题。(整数的k拆分问题)

最优解问题
最优解问题最有代表性的是背包问题。

问题描述:
有n个重量分别为{w1,w2,…,wn}的物品,它们的价值分别为{v1,v2,…,vn},给定一个容量为W的背包。
设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中,要求选中的物品不仅能够放到背包中,而且重量和为W具有最大的价值。

问题解析:
对于可行的背包装载方案,背包中物品的总重量不能超过背包的容量。 最优方案是指所装入的物品价值最高,即 v1x1+v2x2+…+vn*xn(其中xi取0或1,取1表示选取物品i)取得最大值。

问题求解:
设置二维动态规划数组dp,dp[i][r]表示背包剩余容量为r(1≤r≤W),已考虑物品1、2、…、i(1≤i≤n)时背包装入物品的最优价值。显然对应的状态转移方程如下:
dp[i][0]=0(背包不能装入任何物品,总价值为0) 边界条件dp[i][0]=0(1≤i≤n)―边界条件

dp[0][r]=0(没有任何物品可装入,总价值为0) 边界条件dp[0][r]=0(1≤r≤W)―边界条件

dp[i][r]=dp[i-1][r] 当r<w[i]时,物品i放不下

dp[i][r]= MAX{dp[i-1][r],dp[i-1][r-w[i]]+v[i]} 否则在不放入和放入物品i之间选最优解

由此也易得dp[n]W]为所求。

附上代码:

void Knap()			
{  int i,r;
   for (i=0;i<=n;i++)	
	      dp[i][0]=0;
   for (r=0;r<=W;r++)	
	      dp[0][r]=0;
   for (i=1;i<=n;i++)
   {  for (r=1;r<=W;r++)
        if (r<w[i])
           dp[i][r]=dp[i-1][r];
        else
           dp[i][r]=max(dp[i-1][r],dp[i-1][r-w[i]]+v[i]);
   }
}

解的个数问题
典型问题:整数的k项拆分。

问题描述:
一个正整数x拆成k个正整数,k个正整数的和为x,k个正整数可以重复。
(5=1+1+3与5=1+3+1为同解)

问题解析:
问题很简单,即找出x1,x2,x3,…,xk的和等于x,求有多少组这样的x1,x2,x3,…,xk满足条件。

问题求解:
设置二维动态规划数组dp,dp[i][r]表示i的r拆分,显然对应的状态转移方程如下:

dp[i][0]=0(一个整数的0拆分等于0) 边界条件dp[i][0]=0(1≤i≤n)―边界条件

dp[0][r]=0(0的r拆分为0) 边界条件dp[0][r]=0(1≤r≤W)―边界条件

dp[i][r]=0 当i<k时。 i不可能拆出k项来。

dp[i][r]=dp[i][r-1]+1 当i=k时。(多出来的那一种组合全为1)

dp[i][r]=dp[i-1][r-1]+dp[i-r][r] 当i>k时。(即含1的情况和不含1的情况的和)

所以dp[x][k]为所求。

附上代码:

void DP()
{
    int i=0,j=0;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=k;j++)
		{
			if(j==1)
			{
				dp[i][j]=1;
				continue;
			}
			if(i==j)
			{
				dp[i][j]=1;
				continue;
			}
			if(i<j)
				dp[i][j]=0;
			else
				dp[i][j]=dp[i-1][j-1]+dp[i-j][j];

		}

	}
}

从上面的总结可以看出,动态规划求解问题的关键在于状态转移方程的归纳,也就是dp数组的求解,方法很多,我觉得逐步分析是最可行的,有从前向后推导也可以从后向前推导。

在这里另外介绍一下滚动数组:(关系到dp数组空间的浪费)
从上面的问题可以看出,每次求解过程中只涉及到了i,i-1两个层次,求出来的解算一个层次,一共就三个层次,而我们却用了大量的空间储存了多余的解,由此引入滚动数组。
以斐波那契数列求解为例:

int Fib2(int n)		
{  int dp[3];
   dp[1]=1; dp[2]=1;
   for (int i=3;i<=n;i++)
      dp[i % 3]=dp[(i-2)%3]+dp[(i-1)%3];
   return dp[n%3];
}

很多二维数组在求解过程中都可以用滚动数组来代替(是个强有力的办法)。

dp很强也很难,多加练习!!!

问题描述: 假设有一个能装入总体积为T的背包和n件体积分别为w1 , w2 , … , wn物品,能否从n件物品中挑选若干件恰好装满背包,即使w1 +w2 + … + wn=T,要求找出所有满足上述条件的解。例如:当T=10,各件物品的体积{1,8,4,3,5,2}时,可找到下列4组解: (1,4,3,2) (1,4,5) (8,2) (3,5,2)。 问题提示: 可利用回溯法的设计思想来解决背包问题。首先将物品排成一列,然后顺序选取物品装入背包,假设已选取了前i 件物品之后背包还没有装满,则继续选取第i+1物品,若该件物品"太大"不能装入,则弃之而继续选取下一件,直至背包装满为止。但如果在剩余的物品中找不到合适的物品以填满背包,则说明"刚刚"装入背包的那件物品"不合适",应将它取出"弃之一边",继续再从"它之后"的物品中选取,如此重复,直至求得满足条件的解,或者无解。 题目之二: 问题描述: 假设有n件物品,这些物品重量分别是W1 , W2 , … , Wn物品价值分别是V1,V2, …,Vn。求从这n件物品中选取一部分物品的方案,使得所选中的物品的总重量不超过限定的重量W(W<∑Wi, i=1,2,┅,n),但所选中的物品价值之和为最大。 问题提示: 利用递归寻找物品的选择方案。假设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[]中,该方案的总价值保存于变量max_value中。当前正在考察新方案,其物品选择情况保存于数组eop[]中。假设当前方案已考虑了i-1物品,现在要考虑第i件物品:当前方案已包含的物品重量之和为tw;因此,若其余物品都选择是可能的话,本方案所能达到的总价值的期望值设为tv。引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值max_value时,继续考察当前方案已无意义,应终止当前方案而去考察下一个方案。 第i件物品的选择有两种可能: ① 物品i被选择。这种可能性仅当包含它不会超过方案总重量的限制才是可行的。选中之后继续递归去考虑其余物品的选择; ② 物品i不被选择。这种可能性仅当不包含物品i也有可能找到价值更大的方案的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值