递归到动态规划

首先来看一个例子:数字三角形

题目链接

Description

下图中给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。 注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的数或者右边的数。

Input

输入有多组测试数据,输入以EOF为结束,对于每一组测试数据,输入的第一行是一个整数N (1 < N <= 100),给出三角形的行数。下面的N行给出数字三角形。数字三角形上的数的范围都在0和100之间。

Ouput

对于每一组测试数据输出一行,先输出Triangle #i:,i从1开始,然后输出最大和的值。

Sample Input

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

Sample Output

Triangle #1: 30
首先是递归的程序:
#include <iostream>
using namespace std;
int local[102][102];
int n;
int sumMax(int i,int j){
	if(i==n){
		return local[i][j];
	}else{
		return max(sumMax(i+1,j),sumMax(i+1,j+1))+local[i][j];
	}
};
int main()
{
	int sum=0,id=1;
	while(cin>>n){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=i;j++){
				cin>>local[i][j];
			}
		}
		sum=sumMax(1,1);
		cout<<"Triangle #"<<id++<<": "<<sum<<endl;
	} 

	return 0;
}

虽然这个题目是AC的但是我们不妨看一看这个题目的时间复杂度是多少?

我们先来模拟一下这个题目的时间复杂度:


显然,上图中可以发现很多递归的点是有重复计算的。

如果当n=100的时候那么也就是相当于运算了2^100次方次,这会是多么恐怖!显然是不可取的。

那么我们有什么优化的方法呢?

优化方案:

把已经计算过的递归点存储起来,使每个点只需要递归计算一次:

#include <iostream>
using namespace std;
int local[102][102];
int SumMax[102][102];//用来表示i,j到最底层的最大值 
int n;
int sumMax(int i,int j){ 
	if(SumMax[i][j]!=-1){//先判断,如果这个 有计算过就直接返回 
		return SumMax[i][j];
	}
	if(i==n){
		return local[i][j];
	}else{
		return max(sumMax(i+1,j),sumMax(i+1,j+1))+local[i][j];
	}
	return SumMax[i][j];
};
int main()
{
	int sum=0,id=1;
	while(cin>>n){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=i;j++){
				cin>>local[i][j];
				SumMax[i][j]=-1;//也就是说这个坐标点没有计算过 
			}
		}
		sum=sumMax(1,1);
		cout<<"Triangle #"<<id++<<": "<<sum<<endl;
	} 

	return 0;
}

这样每个坐标到只递归了一次,因此这样也就减少递归的次数,这样的时间复杂度为O(n^2)。

如果是100层的话,也就只有计算了100^2次,显然是更好的算法,那么是否还有更加节省时间的方法呢?

现在我们把这个递归的算法转化成逆推,这样的话我们就可以减少最底层的时间量O(n^2-n)的时间复杂度会稍微小一点


代码如下:

#include <iostream>
using namespace std;
int local[102][102];
int SumMax[102][102];//用来表示i,j到最底层的最大值 
int n;
int main()
{
	int sum=0,id=1;
	while(cin>>n){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=i;j++){
				cin>>local[i][j];
			} 
		}
		for(int j=1;j<=n;j++){
			SumMax[n][j]=local[n][j]; 
		}
		for(int i=n-1;i>=1;i--){
			for(int j=1;j<=i;j++){
				SumMax[i][j]=max(SumMax[i+1][j],SumMax[i+1][j+1])+local[i][j]; 
			}
		} 
		sum = SumMax[1][1];
		cout<<"Triangle #"<<id++<<": "<<sum<<endl;
	} 
	return 0;
}

实际上我们还可以对上述代码进行一次空间优化:

其实没必要用二维数组来存储每一个SumMax(i,j),只需要从底层一行行向上递推




















猜你喜欢

转载自blog.csdn.net/yky__xukai/article/details/80294486