用动态规划法求解数塔问题(三角形最小路径)

本文详细探讨了数塔问题的多种解决策略,包括递归、备忘录方法、逆序解法及顺序求解,对比分析了不同算法的优缺点,并提供了具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、求解数塔问题(三角形最小路径)
1、问题描述
在这里插入图片描述
2、递归求解
(1)分析
在这里插入图片描述
(2)问题求解在这里插入图片描述
(3)算法

#define MAX 101
int a[MAX][MAX];
int n;
int MaxSum(int i, int j)
{
	if(i==n)
	return a[i][j];
	int x = MaxSum(i+1,j);
	int y = MaxSum(i+1,j+1);
	return max(x,y)+a[i][j];
}
int main()
{
	int i,j;
	cin >> n;
	for(i=1;i<=n;i++)
	for(j=1;j<=i;j++)
	cin >> a[i][j];
	cout << MaxSum(1,1) << endl;
	}
		

(4)算法分析
在这里插入图片描述
采用递归算法实现,但由于子问题重叠,存在重复的计算!

3、备忘录方法求解
(1)分析
① 如果每算出一个MaxSum(i,j)就保存起来,下次用到其值的时候直接取用,则可免去重复计算。
造表
设置数组dp,首先初始化dp的所有元素为特殊值-1,每算出一个MaxSum(i,j)用dp[i][j]存放其值,当dp[i][j]不为-1时表示对应的子问题已经求解,直接返回结果。
③ 因为数塔的数字总数是 n(n+1)/2,每个数字求一次,可以用O(n2)时间完成。
(2)备忘录方法

  • 动态规划有两种求解思路:
    自顶向下——记忆递归型动归(备忘录方法)
    自底向上——动态递推型动归(动态规划)
  • 备忘录方法(memorization method)
    ①这种方法是一种递归算法,其执行过程也是自顶向下的,但当某个子问题解求出后,将其结果存放在一张表(dp)中,而且相同的子问题只计算一次,在后面需要时只有简单查表,以避免大量的重复计算。
    ②备忘录方法是一般动态规划方法的变形,与一般动态规划算法不同的是,备忘录方法的递归方式是自顶向下的,而一般动态规划算法则是自底向上的
    (3)算法
#define MAX 101
int a[MAX][MAX];
 int n;
int dp[MAX][MAX];
int MaxSum(int i, int j)
{
	if( dp[i][j] != -1 )//之前已经求过
	      return dp[i][j];
	if(i==n) 
	      dp[i][j] = a[i][j];
	else 
	{
		int x = MaxSum(i+1,j);
		int y = MaxSum(i+1,j+1);
		dp[i][j] = max(x,y)+a[i][j];
		//记录每个子问题的结果
	}
	return dp[i][j];
}
int main()
{    
        int i,j; 
        cin >> n;
	for(i=1;i<=n;i++)
	for(j=1;j<=i;j++) 
	{
		cin >> a[i][j];
		dp[i][j] = -1;
	}
	cout << MaxSum(1,1);
}
		

(4)算法分析
在这里插入图片描述
4、递归到动规的一般转化方法
在这里插入图片描述
5、逆序解法求解数塔问题
(1)分析在这里插入图片描述
在这里插入图片描述
(2)算法

  1. 初始化数组dp的最后一行为数塔的底层数据:
    for (i = 1; i < =n; i++)
    dp[n][i] = a[n][i];
  2. 从第n层开始直到第1层对dp[i][j]执行下述操作:
    2.1 dp[i][j]=max{dp[i+1][j],dp[i+1][j+1]}+a[i][j];
    2.2 如果选择下标j的元素,则next[i][j]=j,否则next[i][j]=j+1;
  3. 输出最大路径和dp[1][1];
  4. 根据next数组确定每一层决策的列下标,输出路径信息;
#define MAX 101
int a[MAX][MAX]; 
int n;
int dp[MAX][MAX],next[MAX][MAX];
int main() 
{       int i,j;   
        cin >> n;
	for(i=1;i<=n;i++)//输入数塔
	     for(j=1;j<=i;j++)     
	            cin >> a[i][j];
	for( i = 1;i <= n; ++ i )   
	      dp[n][i] = a[n][i];//初始化边界
	for(  i = n-1; i>= 1; --i )//逆序递推
	      for(  j = 1; j <= i; ++j )
	            if(dp[i+1][j]>dp[i+1][j+1])
	            {     
	                   dp[i][j] =dp[i+1][j] + a[i][j]; 
	                   next[i][j]=j;    
	             }
	             else
	            {     
	                    dp[i][j] =dp[i+1][j+1] + a[i][j]; 
	                    next[i][j]=j+1;  
	             }
	printf("路径为:%d", a[1][1]);         //输出最顶层数字
	 j = next[1][1];   //顶层决策是选择下一层列下标为next[1][1]的元素
	 for (i = 2; i <= n; i++)
	 {     
	         printf("-->%d", a[i][j]);    
	          j = next[i][j];        //本层决策是选择下一层列下标为next[i][j]的元素 
           }
	   printf("\n最大数值和为:%d", dp[1][1] );
}
	

(3)算法分析
在这里插入图片描述
(4)空间优化
在这里插入图片描述
在这里插入图片描述

#define MAX 101
int a[MAX][MAX];
int n; int * dp;
int main()
{       int i,j;
	cin >> n;
	for(i=1;i<=n;i++)
	   for(j=1;j<=i;j++)
	       cin >> a[i][j];
	dp = a[n]; //dp指向第n行
	for( int i = n-1; i>= 1; --i )
	    for( int j = 1; j <= i; ++j )
	         dp[j] = max(dp[j],dp[j+1]) + a[i][j];
	 cout << dp[1] << endl;
}
	

6、顺序求解数塔问题
(1)分析
在这里插入图片描述
在这里插入图片描述
(2)算法

int a[MAXN][MAXN]; //问题表示
int n;
int ans=0; //求解结果表示
int dp[MAXN][MAXN];
int pre[MAXN][MAXN];
int Search()   //求最大和路径ans
{  int i,j;
   dp[1][1]=a[1][1];
   for(i=2;i<=n;i++)  //考虑第1列的边界
   {  dp[i][1]=dp[i-1][1]+a[i][1];
      pre[i][1]=i-1;
   }
   for (i=2;i<=n;i++)  //考虑对角线的边界
   {  dp[i][i]=a[i][i]+dp[i-1][i-1];
      pre[i][i]=i-1;
   }
   for(i=3;i<=n;i++) //考虑其他有两条达到路径的结点
   {  for(j=2;j<i;j++)
         if(dp[i-1][j-1]<dp[i-1][j])
         {  pre[i][j]=j-1;
            dp[i][j]=a[i][j]+dp[i-1][j-1];
         }
         else
         {  pre[i][j]=j;
            dp[i][j]=a[i][j]+dp[i-1][j];
         }
   }
   ans=dp[n][1];
   int k=0;
   for (j=2;j<=n;j++)  //求出最大ans和对应的列号k
   {  if (ans<dp[n][j])
      {  ans=dp[n][j];
         k=j;
      }
   }
   return k;
}
int main()
{  int k;
   memset(pre,0sizeof(pre));
   memset(dp,0sizeof(dp));
   scanf("%d"&n);   //输入三角形的高度
   for (int i=1;i<=n;i++)  //输入三角形
      for (int j=1;j<=i;j++)
         scanf("%d"&a[i][j]);
   k=Search();   //求最大路径和
   Disppath(k);   //输出正向路径
   printf(%d\n”,ans);  //输出最大路径和
   return 0;
}

(3)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值