算法4——数塔问题(DP算法)

Problem Description

在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的:
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

已经告诉你了,这是个DP的题目,你能AC吗?

Input

输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。

Output

对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。

Sample Input

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

Sample Output

30

    本题题意很好理解,就是从最顶出发,一直到最底,哪条线路所有值的和最大,就输出这个值。

    以下解法是从最底层倒推到第一层,其实也可以从第一层推到最底层~

    源码如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int d[103];
	int a[103][103];
	int c,n,i,j;
	scanf("%d",&c);	//输入用例数目
	while(c--)
	{
		scanf("%d",&n);//输入用例的行数
		for(i=0;i<n;i++)//输入所有的数据
			for(j=0;j<=i;j++)
				scanf("%d",&a[i][j]);
		for(j=0;j<n;j++) //将最后一行数据存入数组d
			d[j]=a[n-1][j];
		for(i=n-2;i>=0;i--)/*从最后倒数第二行开始,进行累加,核心代码仅此3行*/
			for(j=0;j<=i;j++)
				d[j]=a[i][j]+(d[j]>d[j+1]?d[j]:d[j+1]);/*每次迭代将数据存在d中*/
		printf("%d\n",d[0]);
	}
}

这样的算法简洁易懂,下面再附上一个代码片段,通过增加一个数组做标记可以将路径上的元素也轻松的打印出来

#include <stdio.h> 
#define MAX_ARRAY_SIZE 20
int data[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE];//存放最初的数据
int cost[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE];//存放各个子问题的最优解
int mark[MAX_ARRAY_SIZE][MAX_ARRAY_SIZE];//存放输出最优解方案标志
int main(int argc,char *argv[])
{
	//初始化data
	int n;
	scanf("%d",&n);
	int i,j;
	for(i=0;i<n;i++){
		for(j=0;j<=i;j++){
			scanf("%d",&data[i][j]);
		}
	}
	//dp初始化
	for(i=0;i<n;i++){
		cost[n-1][i]=data[n-1][i];
	}
	//dp运行
	for(i=n-2;i>=0;i--){
		for(j=0;j<=i;j++){
			if(cost[i+1][j]>cost[i+1][j+1]){
				cost[i][j]=data[i][j]+cost[i+1][j];
				mark[i][j]=0;
			}else{
				cost[i][j]=data[i][j]+cost[i+1][j+1];
				mark[i][j]=1;
			}
		}
	}
	printf("max: %d\n",cost[0][0]);
	for(i=0,j=0;i<n;i++){
		printf("line:%d,value: %d\n",i+1,data[i][j]);
		if(mark[i][j]==1)
			j=j+1;
	}
	return 0;
}

下面再以C++来一轮代码,亮点个人感觉在输出那个最优路径的小函数(即下面print_result()函数),通过倒退,相比于上面借助一个标记数组 是一种不一样的思维,值得学习

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50;
int data[N][N];//存储数塔原始数据
int dp[N][N];//存储动态规划过程中的数据
int n;//塔的层数
void tower_walk()/*动态规划实现数塔求解*/
{
    for (int i = 0; i < n; ++i)
        dp[n - 1][i] = data[n - 1][i];// dp初始化
    int temp_max;
    for (int i = n - 2; i >= 0; --i)
        for (int j = 0; j <= i; ++j)
        {
            // 使用递推公式计算dp的值
            temp_max = max(dp[i + 1][j], dp[i + 1][j + 1]);
            dp[i][j] = temp_max + data[i][j];
        }
}
void print_result()
{
    cout << "最大路径和:" << dp[0][0] << '\n';
    int node_value;
    // 首先输出塔顶元素
    cout << "最大路径:" << data[0][0];
    int j = 0;
    for (int i = 1; i < n; ++i)
    {
        node_value = dp[i - 1][j] - data[i - 1][j];
        /* 如果node_value == dp[i][j]则说明下一步应该是data[i][j];如果node_value == dp[i][j + 1]则说明下一步应该是data[i][j + 1]*/
        if (node_value == dp[i][j + 1]) ++j;
        cout << "->" << data[i][j];
    }
    cout << endl;
}
 
int main()
{
    cout << "输入塔的层数:";
    cin >> n;
    cout << "输入塔的节点数据(第i层有i个节点):\n";
    for (int i = 0; i < n; ++i)
        for (int j = 0; j <= i; ++j)
            cin >> data[i][j];
    tower_walk();
    print_result();
}

猜你喜欢

转载自blog.csdn.net/qq_41345173/article/details/81807792