动态规划专题一:数字三角形问题(多种方法)

舒适的观看题目链接
给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

       7
     3   8
   8   1   0
 2   7   4   4
4   5   2   6   5

输入格式
第一行包含整数 n,表示数字三角形的层数。
接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。
输出格式
输出一个整数,表示最大的路径数字和。
数据范围
1≤n≤500,
−10000≤三角形中的整数≤10000
输入样例:

5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5

输出样例:

30

首先正常思路是从上往下考虑,但是考虑到发散问题,利用回溯法求解
但是时间很慢,一个n层的数字三角形有 2^n-1 条路线
回溯法:

  当把问题分成若干步骤并递归求解时,如果当前步骤没有合法的选择,则函数将
  返回上一级递归进行调用,这种现象称之为回溯。

当我们递归枚举每一种路线时,都要回溯寻求最优解。
所以导致了时间上的缓慢。
为了得到高效的算法利用抽象的思想进行思考
某一点 (i,j)
从这一点考虑两层的路线设d(i,j)表示从(i,j)到最底层的和a(i,j)表示这点的值
那么就可以得到式子
d(i,j)=a(i,j)+max(d(i+1,j),d(i+1,j+1))
这个式子也被称为状态转移方程。
当然,这是部分全局最优解想要得到全局最优解还是需要进行计算处理的。
处理的方法及过程:
(1).递归计算
代码如下:

int solve(int i,int j)
{
	return a[i][j] + (i == n ? 0 : max(solve(i+1,j),solve(i+1,j+1)));
}

做法是正确的,但是效率相比也比较低,原因在于相同子问题重复计算多次
ex:

                             ----->(3,1)
           ----->(2,1)                        ----->(4,2)****
                             ----->(3,2)****
(1,1)                                       ----->(4,3)****
                                              ----->(4,3)****
                             ----->(3,2)****
           ----->(2,2)                         ----->(4,3)****
                             ----->(3,3)

每一个带了****号的都是重复的,因为所以效率较低,不与采用。
(2).递推计算:
程序如下:

int  i,j;
for(j = 1; j <= n; j++) d[n][j] = a[n][j];
for(i = n-1; i >= 1; i--)
   for(j = 1; j <= i; j++)
   {
   		d[i][j] = a[i][j] + max(d[i+1][j],d[i+1][j+1]);
   }

时间复杂度显然是o(n^2)为什么可以这样计算呢?
i是逆序枚举的
因此在计算 d[i][j] 时 d[i+1][j] 和 d[i+1][j+1] 已经算出来了
注意
可以用递推法计算状态转移方程,递推的关键是边界和计算顺序
一般递推时间复杂度为:
状态总数 * 每个状态的决策个数 * 决策时间
如果不同状态的决策个数不同,需要具体问题具体分析。

(3).记忆化搜索
代码如下:

memset(d, -1 , sizeof d);
int solve(int i,int j)
{
    if(d[i][j] >= 0) return d[i][j]; 
	return d[i][j] = a[i][j] + (i == n ? 0 : max(solve(i+1,j),solve(i+1,j+1)));
}

上述程序依然是递归的,但是只要把所有的d初始化为-1,那么可通过判断d[i][j]
是否>=0来判断是否已经被计算过。
最后,不要忘记在计算之后把他保存在d[i][j]中
上述程序的方法称为记忆化,能够有效保证每个节点只访问一次。
时间复杂度均为O(n^2)。
利用记忆化搜索的方法计算状态转移方程,不必事先确定各状态的计算顺序,但是却需要记录每个状态是否被计算过。

相信你已经对状态和状态方程已经有比较强的了解了,那么动手实践一下吧!
写题去!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值