动态规划入门二

本文主要通过一个题目来讲解动态规划的基本设计思路:

数字三角形:
题目:给出如下图所示的等腰三角形的行数 和组成三角形的数字,从顶点往下走有两条路,左下或右下,输出路径最长的路。
5
7
3   8
8   1   0
2   7   4   4
4   5   2   6   5

动态规划的关键就在于找到“状态”及“状态转移方程”。
一、状态
百度百科这样定义‘状态’:状态是人或事物表现出来的形态。是指现实(或虚拟)事物处于生成、生存、发展、消亡时期或各转化临界点时的形态或事物态势。简单来说就是,处于特定时间、状况下事物的一种形态。而动态规划中所讲的‘状态’可以理解为某个变量(如数组中元素)在特定条件下的‘值’。
二、转态转移方程——连接变量的桥梁

如果说‘状态’是某个变量的值,那么‘状态转移方程’则表明了(变量)‘值’与(变量)‘值’之间的关系。

例如:假设有二维数组dp[][]。若dp[i][j]  = 10 ,则其位置:(i,j)、值:10 都可以是状态。在条件一下,dp[i][j] 的值是0,而在条件二下dp[i][j]的值是它行列坐标值加一和行坐标值加一中较大的那个。如下图:
这就是状态转移方程,即在有条件(策略)的选择下使得各个变量(状态)产生某种关系的等式。

首先,必须保存题目中的数据,显然用二维数组最适合:
定义一个triangle[100][100]的二维数组,则题目中的‘状态’为triangle[i][j]的值,考虑它是三角形中普通的一员(假设就是triangle[3][2])。其次,要求最大值我们可以从两方面考虑,从顶往下走,或是从底往上走。无论怎么走我们必须进行选择,是往左下(上)走还是往右下(上)走,为方便起,我们决定从下往上走,此时我们需要保存走过的路径长度,由于每次走都有两个选择,所以需要用到二维数组来保存所有可能的长度,再进行选择。我们不是现成就有一个二维数组吗?可以直接利用这个二维数组吗?答案是肯定的,有人就会质疑了,这个二维数组不是用来保存原始数值的吗?这样做会覆盖掉原来的值吧。虽然覆盖掉了,但是题目只要求最长的路径即可不需要保存中间数值。当然,再利用一个二维数组也是可以的,as we all know,勤俭节约是中华民族的传统美德,所以我们就只用一个二维数组。初始值triangle[3][2] == 1;(数组从1开始)往下走,往左下7,右下4,显然,为保证结果最大,每步的结果也应最大,(最优子结构/全局最优解包含局部最优解),该状态的值为下一行较大者加到该状态,所以得到方程:triangle[3][2] +=max(triangle[4][2],trianle[4][3]);
推广有:triangle[i][j] += max(trianle[i+1][j],trianle[i+1][j+1]);
这里就涉及到数组边界的处理了。
由上面的思路可知:最后一排的值就应该是他们本身,也就是说,方程应该从倒数第二行开始。
for(i = n-1 ; i>=1 ; i--)//往上走,注意边界
for(j =1  ;  j<=i  ;  j++)
{                 
trianlge[i][j] +=  max(trianle[i+1][j] , trianle[i+1][j+1]); //每次往上走,都选取最大的值
过程如下图所示:

左下和右下进行比较: 4和5、5和2、2和4、6和5,选取大的加到上面


把大的结果加上原来的值从而覆盖掉原来的值,继续进行直到到达顶端
代码如下:
#include<stdio.h>
int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    int n;
    int i, j;
    int triangle[100][100];
    scanf("%d",&n);
    for(i = 1 ; i<=n ; i++)
        for(j = 1 ; j<=i ; j++)
            scanf("%d",&triangle[i][j]);//输入
    for(i = n-1 ; i>=1 ; i--)
        for(j = 1 ; j<=i ; j++)
            triangle[i][j] += max(triangle[i+1][j],triangle[i+1][j+1]);//状态转移方程
    printf("%d",triangle[1][1]);//输出顶点
return 0;
}
如有错误欢迎指正。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值