动态规划法(dynamicprogramming)也是将待求解问题分解成若干个子问题,但是子问题之间往往不是相互独立的,
如果用分治法求解,这些子问题的重叠部分被重复计算多次。
动态规划法将每个子问题求解一次并将其解保存在一个表格(通常采用数组)中,当需要再次解此子问题时,只是简单地通过查表获得该子问题的解,从而避免了大量重复计算。
动态规划法的一般过程:
一般来说,动态规划法的求解过程由以下三个阶段组成。
(1)划分子问题:将原问题分解为若干个子问题,并且子问题之间具有重叠关系;
(2)动态规划函数:根据子问题之间的重叠关系找到子问题满足的递推关系式(称为动态规划函数);
(3)填写表格:设计表格的形式及内容,根据递推式自底向上计算,实现动态规划过程。
算法设计实例——数塔问题
【问题】 如图2-8所示的一个数塔,从数塔的顶层出发,在每一个结点可以选择向左走或向右走,一直走到底层,要求找出一条路径,使得路径上的数值和最大。
例如,所示数塔的最大数值和是:8+15+9+10+18=60。
从5层数塔的顶层(设顶层为第1层)出发,下一层选择向左走还是向右走取决于两个4层数塔的最大数值和
如何找到子问题满足的动态规划函数呢?
显然,动态规划的求解需要从底层开始进行决策。
底层的每个数字可以看做1层数塔,则最大数值和就是其自身;
第4层的决策是在最底层决策的基础上进行求解的,可以看做4个2层数塔,对每个数塔进行求解;
第3层的决策是在第4层决策的基础上进行求解的,可以看做3个2层的数塔,对每个数塔进行求解,
最后第1层的决策结果就是数塔问题的整体最优解。
【程序】 主函数首先初始化数组d[n][n]为n层数塔的数字,然后调用函数DataTorwer求解最大数值和并输出相应的路径。程序如下:
#include <stdio.h>
const int n = 5; // 设塔是5层
int DataTorwer(int d[n][n]); // 函数声明,求解n层数塔
int main(void)
{
int d[n][n]={{8}, {12,15}, {3,9,6}, {8,10,5,12}, {16,4,18,10,9}};
printf("最大数值和为: %d\n", DataTorwer(d));
return 0;
}
int DataTorwer(int d[n][n])
{
int maxAdd[n][n]={0}, path[n][n]={0};
int i, j;
for( j=0; j<n; j++)
maxAdd[n-1][j] = d[n-1][j]; // ====> maxAdd[4][] = {16, 4, 18, 10, 9};
for(i=n-2; i>=0; i--) // i = 3 2 1 0
{
for( j=0; j<=i; j++)
{
if( maxAdd[i+1][j] > maxAdd[i+1][j+1])
{
maxAdd[i][j] = d[i][j] + maxAdd[i+1][j];
path[i][j] = j;
}else{
maxAdd[i][j] = d[i][j] + maxAdd[i+1][j+1];
path[i][j] = j+1;
}
}
}
printf("路径为:%d ",d[0][0]);
j = path[0][0];
for( i = 1; i< n; i++)
{
printf(" --> %d ", d[i][j]);
j = path[i][j];
}
return maxAdd[0][0];
}