• 动态规划--数字三角形问题


    1. 问题描述

    有一个像这样的数字三角形:
             

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

    从顶点开始,每个数字向下层走只能有左下和右下两个方向,求出到达最后一行时最大的路径之和。

    Input 
    第1 行是数字三角形的行数n,1<= n <=100。

    接下来n行是数字三角形各行中的数字。所有数字在0---99之间。

    比如Input是:

    5

    7

    3 8

    8 1 0

    2 7 4 4

    4 5 2 6 5

    则output是30。

    2. 问题求解

    这是一个典型的动态规划求解问题,因为它符合动态规划所能解决的问题的性质:

    最优子结构性质

    子问题重叠性

    看到这个题目,很容易想起多段图的那个题目,其实道理真的是一样的,唯一的区别就是:一个是从一些顶点到一个终点求最小值;一个是一个起点到一些点求最大值。

    所以状态和转移方程都可以模仿多段图的解法:

    [f(i,j) = max { f(i - 1,j - 1),f(i - 1,j)}  + {C_{ij}}] [0 le i < n,0 < j < i]

    其中i表示该点所在三角形的行数(行数从0开始算),j表示该点所在某行的第几个,也就是列数(列数从0开始计算)。

    注意:转移方程中的i,j在代码计算的时候不能越界。所以每一行的两端的数字的f函数要单独计算,只有一种决策才能到达它们。

    3. 代码实现

    #define MAX 100
    #define getMax(x,y) (x>y ? x : y)
    #include <stdio.h>
    
    void main(void)
    {
    	freopen("in.txt", "r", stdin);
    	freopen("out.txt", "w", stdout);
    
    	//初始化数组的元素全部为0
    	int path[MAX][MAX] = {0};
    	int dist[MAX][MAX] = {0};
    
    	//为三角形赋值
    	int i = 0;
    	int j = 0;
    	int n = 0;	//三角形的行数
    	scanf("%d", &n);
    
    	for(i= 0; i < n; i++)
    	{
    		for(j = 0; j <= i; j++)
    		{
    			scanf("%d", &path[i][j]);
    		}
    	}//for
    
    	//为了防止转移方程越界,每行的开头和结尾首先算出最长路径
    	dist[0][0] = path[0][0];
    	for(i = 1; i < n; i++)
    	{
    		dist[i][0] = dist[i-1][0] + path[i][0];
    	}//for
    
    	for(i = 1; i < n; i++)
    	{
    		dist[i][i] = dist[i-1][i-1] + path[i][i];
    	}
    
    	//计算出中间每个节点的最长路径
    	for(i = 2; i < n; i++)
    	{
    		for(j = 1; j < i; j++)
    		{
    			dist[i][j] = getMax(dist[i-1][j-1], dist[i-1][j]) + path[i][j];
    		}
    	}
    
    	//找到最长的路径
    	int maxSum = 0;
    	for(j = 0; j < n; j++)
    	{
    		if(maxSum < dist[n-1][j])
    		{
    			maxSum = dist[n-1][j];
    		}
    	}//for
    
    	printf("%d", maxSum);
    
    
    	fclose(stdin);
    	fclose(stdout);
    	return;
    }
    

      测试数据:

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

    4. 空间压缩

    上面的算法的时间复杂度是O(n2),空间复杂度同原始的数组一样大小。下面根据前面的矩形的最短路径的动态规划的空间压缩方法,对这个问题的算法的空间复杂度进行压缩。同时如果从上往下的计算起始点到终止点的路径和,为了防止数组边界越界,要单独的处理两个边界,所以可以采用从下往上的方式处理。

    int min(int param1, int param2)
    {
    	return param1 < param2 ? param1 : param2;
    }
    
    int minTotal(vector<vector<int> >& triangle)
    {
    	int i,j;
    
    	for(i = triangle.size(); i >= 0; i--)
    	{
    		for(j = 0; j < i + 1; j++)
    		{
    			triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]);
    		}
    	}
    
    	return triangle[0][0];
    }
    

      这种改进的时间复杂度没有改变,但是空间复杂度减少到了O(1),但是原数组的数据被修改掉了。当然也可以用一个O(n)大小的数组记录每一行的最短路径和,然后利用从下向上滚动的方式,数组的大小逐渐减少1,这样就实现了在不改变原数组的条件下,空间复杂度从原来的O(n2)减低到了O(n)。

  • 相关阅读:
    MYSQL利用merge存储引擎来实现分表
    IoC原理及实现
    prometheus + grafana安装部署(centos6.8)
    PHP Laravel定时任务Schedule
    排序算法杂谈(三) —— 归并排序的非递归实现
    排序算法杂谈(二) —— 冒泡排序的递归实现
    排序算法杂谈(一) —— 量化数组的有序程度
    排序算法(七) —— 快速排序
    排序算法(六) —— 归并排序
    排序算法(五) —— 堆排序
  • 原文地址:https://www.cnblogs.com/stemon/p/4596701.html
Copyright © 2020-2023  润新知