动态规划——数字三角形

        学习算法,个人觉得还是对照着例题来理解效果做好,算法概念太学术味太浓,个人不太喜欢。今天介绍一个动态规划入门例题。这道题来自北大的POJ。题目如下:

 

数字三角形(POJ1163)

    

在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99

    输入格式:

    5      //表示三角形的行数    接下来输入三角形

    7

    3   8

    8   1   0

    2   7   4   4

    4   5   2   6   5

    要求输出最大和

 

我们来分析一下这道题该怎么做

首先肯定要一个二维数组来存放三角形

然后我们用D( r, j) 来表示第 r 行第  j 个数字(r , j1开始算)

我们用maxSum(r,j)表示D(r,j)到底边各条路径的最大值

最后就是要求D(1,1)

我们可以用递归来求解

D(r,j)出发,下一步只能走D(r+1,j)D(r+1,j+1)  ,可以得到如下递归式

if ( r == N)                
	MaxSum(r,j) = D(r,j)  
else      
	MaxSum( r, j) = Max{ MaxSum(r+1,j), MaxSum(r+1,j+1) } + D(r,j) 

由上述递归式可以得到如下的代码:



#include
#include
using namespace std;

int D[50][50];
int n;

int dp(int r, int j) {
	if (r == n) {
		return D[r][j];
	}
	int x = dp(r + 1, j);
	int y = dp(r + 1, j + 1);
	return max(x, y) + D[r][j];
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			cin >> D[i][j];
		}
	}
	cout << dp(1, 1) << endl;

	system("pause");
	return 0;
}

 

不过这段代码中有很多重复计算的地方,看图

当计算7的时候,要往下依次递归计算3、8,计算3的时候要递归计算8、1,计算8(7下面右边 那个)的时候又要计算一次1,其它的以此类推。这要就导致了很多不必要的重复计算,我们可以保留这些计算结果作为判断是否计算过的依据,

 

      改进过后的代码如下:



#include
#include
using namespace std;

int D[50][50];
int maxSum[50][50];
int n;

int dp(int r, int j) {
	if (maxSum[r][j] != -1) {
		return maxSum[r][j];
	}
	if (r == n) {
		maxSum[r][j] = D[r][j];
	}
	else {
		int x = dp(r + 1, j);
		int y = dp(r + 1, j + 1);
		maxSum[r][j] = max(x, y) + D[r][j];
	}
	return maxSum[r][j];
}

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			cin >> D[i][j];
			maxSum[i][j] = -1;
		}
	}
	cout << dp(1, 1) << endl;

	system("pause");
	return 0;
}

 

    因为递归总是需要使用大量堆栈上的空间,很容易造成栈溢出,我们现在就要考虑如何把递归转换为递推,让我们一步一步来完成这个过程。

代码如下:



#include
#include
using namespace std;

int D[50][50];
int maxSum[50][50];
int n;

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			cin >> D[i][j];
		}
	}
	for (int i = 1; i <= n; i++) {
		maxSum[n][i] = D[n][i];
	}
	for (int i = n - 1; i >= 1; i--) {
		for (int j = 1; j <= i; j++) {
			maxSum[i][j] = max(maxSum[i + 1][j], maxSum[i + 1][j + 1]) + D[i][j];
		}
	}
	cout << maxSum[1][1] << endl;
	system("pause");
	return 0;
}

 

     到这里是不是觉得已经到极致了?错,还可以继续优化,不过不是时间上的优化,是空间上的优化,我们可以只用一个一维数组来记录已经得到的值。只要从底层一行行向上递推,那么只要一维数组maxSum[100]即可,即只要存储一行的MaxSum值就可以。看图。

 

     

     依照上面的方式,我们可以写出如下代码: 



#include
#include
using namespace std;

int D[50][50];
int maxSum[50];
int n;

int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= i; j++) {
			cin >> D[i][j];
		}
	}
	for (int i = 1; i <= n; i++) {
		maxSum[i] = D[n][i];
	}
	for (int i = n - 1; i >= 1; i--) {
		for (int j = 1; j <= i; j++) {
			maxSum[j] = max(maxSum[j], maxSum[j + 1]) + D[i][j];
		}
	}
	cout << maxSum[1] << endl;
	system("pause");
	return 0;
}

 

 

至此,该问题已经是最优了。

 

如有错误,请指正。

 

你可能感兴趣的:(动态规划)