动态规划基础

动态规划基础


动态规划的基本要素

  • 最优子结构->保证子结构计算正确
  • 无后效性->保证孩子不会对父亲产生影响
  • 状态转移方程->由子状态转移到父状态

如何设置一个合理的状态是动态规划最难的一步,也只能靠题目的积累。

例题1 :爬楼梯

有100级楼梯,每次只能上1级或2级,一共有多少种不同的方法上楼?

d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i]=dp[i-1]+dp[i-2] dp[i]=dp[i1]+dp[i2]

例题2: 摘花生

d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + s [ i ] [ j ] dp[i][j]=max(dp[i-1] [j],dp[i][j-1])+s[i][j] dp[i][j]=max(dp[i1][j],dp[i][j1])+s[i][j]

例题3:鸡蛋的硬度
  • 题目简述:给出n,m分别代表楼层高度以及鸡蛋个数,问最小的尝试次数来找到鸡蛋的硬度,考虑最坏情况。
  • 解释样例:对于100 2的解释: n + ( n − 1 ) + ( n − 2 ) + . . . + 1 ≥ 100 n+(n-1)+(n-2)+...+1\ge100 n+(n1)+(n2)+...+1100 显然有 n × ( n + 1 ) ÷ 2 ≥ 100 n\times(n+1)\div2\ge100 n×(n+1)÷2100 ,解得 n ≥ 14 n\ge14 n14
  • 思考更展开的问题:我们发现第n颗鸡蛋的作用无非就是帮助第n-1颗鸡蛋缩小范围,不妨根据上面的式子换个角度考虑问题,这个方法的精髓在于始终保持扔了的和没扔的次数指和相同,这样做的好处是充分考虑了最坏情况,(不会透支使用次数来赌)始终把总投掷次数控制在14.再宽泛一点说,对于第n颗鸡蛋使用的次数与留给第n-1颗鸡蛋的次数之和永远相同,相当于平均分。换一个角度理解上面的例子,2颗鸡蛋,14次投掷,最多能测多少层楼,显然是105层(我们已经有了2颗鸡蛋,n次投掷测出的最大楼层数公式 n × ( n + 1 ) ÷ 2 n\times(n+1)\div2 n×(n+1)÷2 ,那么照猫画虎,我们可以推测3颗鸡蛋扔n次,最多能测多少层楼 1 + ( n − 1 ) × n ÷ 2 + 1 + ( n − 2 ) × ( n − 1 ) ÷ 2 + . . . + 1 + 2 × 3 ÷ 2 + 1 + 1 × 2 ÷ 2 + 1 1+(n-1)\times n\div2+1+(n-2)\times (n-1)\div 2+...+1+2\times3\div2+1+1\times2\div2 +1 1+(n1)×n÷2+1+(n2)×(n1)÷2+...+1+2×3÷2+1+1×2÷2+1
  • 推广!推广:现在这个问题已经基本明了了,我们展示了从2颗鸡蛋推到3颗鸡蛋的过程,换句话说,没有2颗鸡蛋的计算就不会有3颗鸡蛋的计算(最优子结构),同时,3颗鸡蛋的计算不会干扰2颗鸡蛋的计算(无后效性),剩下的事情就是来找递推公式了,我们假设我们已经找到了n-1颗鸡蛋投掷k次的答案 p n − 1 k p^{k}_{n-1} pn1k ,我们尝试用n-1颗鸡蛋来扩展到n颗鸡蛋,其实已经相当明了了 p n k = 1 + p n − 1 k − 1 + 1 + p n − 1 k − 2 + . . . + 1 + p n − 1 2 + 1 + p n − 1 1 + 1 p^{k}_{n } = 1+p^{k-1}_{n-1}+1+p^{k-2}_{n-1}+...+1+p^{2}_{n-1}+1+p^{1}_{n-1}+1 pnk=1+pn1k1+1+pn1k2+...+1+pn12+1+pn11+1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>

int dp[102][102];

int main()
{
	int n,m;
	while (scanf("%d%d",&n,&m)==2) {
		memset (dp,0,sizeof(dp));
		
		for (int i=1;i<=n;i++) dp[1][i]=i;
		for (int i=2;i<=m;i++) {  // i颗鸡蛋,扔j次,能测量的最大楼层
			for (int j=1;j<=n;j++) {
				dp[i][j]=dp[i-1][j-1]+dp[i][j-1]+1;
			}
		}
		for (int i=1;i<=n;i++) {
			if (dp[m][i]>=n) {
				std::cout<<i<<std::endl;
				break;
			}
		} 
	}
	
	return 0;
}
例题4 :舔狗舔到最后一无所有

这道题目便是利用辅助数列的思想来进行求解,由于以前讲过多次,不再过多赘述。

总结

动态规划类似于高中数列,对于一个复杂的数列(称之为答案数列),写出通项公式所需要的数学能力是非常强大的,但是我们可以写出递推公式,如果递推公式仍然难写,那我们便可以构造一些辅助数列来解决,辅助数列可以帮助我们很好的写出答案数列的递推公式,在探索辅助数列的过程中我们也可能会发现有些数列不是那么必须,那么便可以省去。可以泛泛地理解,辅助数列越多,答案数列越好写,但是维护辅助数列的过程就越发地冗余。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值