吊打记忆递归动态规划(附带例题讲解POJ 1163)

目录

例题 POJ 1163 数字三角形

方法一 :递归(耗时

方法二:记忆递归

方法三:递推(递归的逆运算

总结 动态规划解题一般思路:

1.将原问题分解为子问题(分治思想)

2.确定状态

3.确定一些初始状态(边界状态)的值

4.确定状态转移方程

 能用动态规划解决问题的特点:

1.问题具有最优子结构的性质

2.无后效性


例题 POJ 1163 数字三角形

1163 -- The Triangle

方法一 :递归(耗时

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
const int N=1e7+10;
int MAXsum(int i,int j);
int n;int a[MAX][MAX];
int main()
{
	cin>>n;
	int i,j;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			cin>>a[i][j];
		}
	}
	cout<<MAXsum(1,1);
	return 0;
}

int MAXsum(int i,int j)
{
	if(i==n)
	return a[i][j];
	else
	{
		int x=MAXsum(i+1,j);
		int y=MAXsum(i+1,j+1);
	 return max(x,y)+a[i][j];
	}
}

方法二:记忆递归

方法一之所以耗时是因为递归出现了重复调用,计算。导致效率低下。所以我们采用一种办法,就是计算过的位置不再计算,将其记住,需要的时候直接调用。

这里使用一个maxsum数组来记录每次计算的结果

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
const int N=1e7+10;
int MAXsum(int i,int j);
int n;int a[MAX][MAX];
int maxsum[MAX][MAX];
int main()
{
	cin>>n;
	int i,j;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			cin>>a[i][j];
			maxsum[i][j]=-1;//-1代表还没计算
		}
	}
	cout<<MAXsum(1,1);
	return 0;
}

int MAXsum(int i,int j)
{
	if(maxsum[i][j]!=-1)//检测到不等于-1说明该位置计算过了
	return maxsum[i][j];
	else if(i==n)
	maxsum[i][j]= a[i][j];
	else
	{
		int x=MAXsum(i+1,j);
		int y=MAXsum(i+1,j+1);
	    maxsum[i][j] = max(x,y)+a[i][j];
	}
	return maxsum[i][j];
}

方法三:递推(递归的逆运算

我们可以直接用双循环,从数据的底部向上地推出每一个数的MaxSum,这样也能避免递归的重复计算

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 101
const int N=1e7+10;
int n;int a[MAX][MAX];
int maxsum[MAX][MAX];
int main()
{
	cin>>n;
	int i,j;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=i;j++)
		{
			cin>>a[i][j];
		}
	}
	for(i=n,j=0;j<=n;j++)
	{
		maxsum[i][j]=a[i][j];
	}
	for(i=n-1;i>=1;i--)
	{
		for(j=1;j<=i;j++)
		{
			maxsum[i][j]=max(maxsum[i+1][j],maxsum[i+1][j+1])+a[i][j];//从下到上进行递推合并
		}
	}
	cout<<maxsum[1][1];
	return 0;
}

总结 动态规划解题一般思路:

1.将原问题分解为子问题(分治思想)

1.把原问题分解为若干个子问题,子问题和原问题相同或类似,通过逐步分解的办法使得原问题规模变小到可以直接解决出子问题,原问题便得到了解决。(在数在三角形中分解到数字三角形的底边,便得到答案了)

2.子问题一旦求出,便被保存,避免再次重复计算,所以每个子问题都只需求一次。(可以用数组保存每次求解子问题的答案)

2.确定状态

所有状态的集合,构成问题的 状态空间 ,状态空间 的大小与动态规划解决问题的时间直接相关。与子问题相关的各个变量的一组取值,称之为一个状态,同样一个状态对应于一个或多个子问题。而某个状态的值一旦确定,那么该子问题便得到了解决。

   我们通常创立一个数组来存放该状态下的值,该值不一定是一个整数或浮点数,可能是需要一个结构才能表示,那么该数组可以是结构体数组。一个状态下的值可以是一个或多个子问题的解

     (数字三角形里共有n(n+1)/2个数据,则对应有这么多状态,确定了每个状态的值,问题便得到了解决)

3.确定一些初始状态(边界状态)的值

比如 ”数字三角形“,我们确定的初始状态就是底边的一行数据,而其对应的就是底边数字本身。然后由已知推未知。

4.确定状态转移方程

定义出了状态,以及在该状态下的值以后,我们要找出不同状态之间要如何迁移。即从一个或多个已知值的状态找出另一个状态的值,那么我们就需要它们之间的关系式递推公式,也称作”状态转移方程“。类似于小学所讲的应用题,由题 目找出数据之间的关系。也就是说,该方程就蕴含在题目当中。

例如例题中数字三角形的状态转移方程就是:

 能用动态规划解决问题的特点:

1.问题具有最优子结构的性质

如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质。比如例题中数字三角形 我们求每个状态的值都必须要求用来求它的状态的值是最优的。

2.无后效性

当前状态的值一旦确定,则此后过程的演变就只和这个状态的值有关,和他怎么来的没有任何关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值