【Triangle 】cpp
题目:
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle
代码:
class Solution {
public:
int minimumTotal(vector<vector<int> > &triangle) {
if (triangle.size()<) return ;
int min_sum = triangle[][];
for ( int i = ; i<triangle.size(); ++i )
{
for (int j = ; j<triangle[i].size(); ++j )
{
if (j==triangle[i].size()-)
{
triangle[i][j] += triangle[i-][j-];
min_sum = std::min(min_sum, triangle[i][j]);
}
else if ( j== )
{
triangle[i][] += triangle[i-][];
min_sum = triangle[i][j];
}
else
{
triangle[i][j] += std::min(triangle[i-][j-], triangle[i-][j]);
min_sum = std::min(min_sum, triangle[i][j]);
} }
}
return min_sum;
}
};
tips:
这种做法时间复杂度O(n),空间复杂度O(1)。
思路很简单,就是遍历每层元素的同时,把这一个元素的值更新为走到该位置的最小路径和。
min_sum这个遍历记录当前最小值,其实只有当遍历到最后一层的时候才有用,偷懒就没有改动了。
但是有个缺点就是把原来的triangle的结构都破坏了,研究一下用O(n)额外空间,但不破坏原有triangle的做法。
=======================================
似乎脑子里有印象:遍历每层数组的时候,可以从后往前算,可以比较顺畅。顺着思路,就写了下面的代码,不破坏triangle的原有结构,也可以在O(n)额外空间的条件下AC。
class Solution {
public:
int minimumTotal(vector<vector<int> > &triangle) {
if (triangle.size()<) return ;
vector<int> extra(triangle.size(),INT_MAX);
extra[] = triangle[][];
for ( int i = ; i<triangle.size(); ++i )
{
for ( int j = triangle[i].size()-; j>=; --j )
{
if ( j== )
{
extra[j] = triangle[i][j] + extra[];
}
else
{
extra[j] = triangle[i][j] + std::min(extra[j-], extra[j]);
}
}
}
int min_sum = extra[];
for ( int i = ; i < extra.size(); ++i ) min_sum = std::min(min_sum, extra[i]);
return min_sum;
}
};
tips:
这里开一个额外vector(即extra),大小为n,数组元素初值都设为INT_MAX(后面解释为什么要设为INT_MAX)。
extra用来存放“到当前层的各个位置的最短路径长度和是多少”。
为什么遍历每层都要从后往前遍历呢?
举例说明如下:
以原题给的case为例
i = 1时
extra == [2,INT_MAX...]
triangle[1] == [3,4]
观察两种遍历triangle[1]的方向:
a) 先遍历triangle[1][0]则更新extra[0]为5,即extra == [5,INT_MAX...]
b) 这时,再遍历triangle[1][1],判断extra[0]与extra[1]哪个小,再与triangle[1][1]相加,再赋值给extra[1]。
这里问题就凸显出来了,此时extra[0]已经不是最开始的2了,已经被我们更新过后丢掉了(此时可以采用补救措施,例如添加一个中间变量tmp之类的)。
现在换一种思路,改变遍历triangle[1]的方向
a) 遍历triangle[1][1],则更新extra[1]为6
b) 这时再遍历triangle[1][0],更新extra[0]为extra[0]+triangle[1][0]=5;结果正确。
这样对比就可以看出来从后向前遍历的好处,因为下一层的数组总比上一层的数组多出来一个元素;因此再更新时,先更新extra的最后一个位置并没有影响到上一轮extra得到的结果(于是也就不用什么tmp中间变量之类的了)。
还有一个细节没有说:为啥初始化extra的时候都初始化为INT_MAX呢?这是为了代码的优雅性。
通过题意我们可以知道,其实每层的最后一个元素j只能由上一层的的最后一个元素j-1得来。
为了保持“extra[j] = triangle[i][j] + std::min(extra[j-1], extra[j]);”的优雅性,因此初始化为INT_MAX;如果j==triangle[i].size()-1的时候,我们已经知道一定是选择extra[j-1]而不是extra[j],因为此时的extra[j]就是INT_MAX。
这样一来,只用处理j==0的一种corner case了。
=================================
看到一种更屌爆的做法,完全不用判断各种corner case,思路如下:
从下往上遍历,牺牲triangle的原有结构。
======================================
第二次过这道题,用O(n)复杂度,单独处理最左边的元素。
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
if ( triangle.empty() ) return ;
vector<int> minPath(triangle.size(),INT_MAX);
minPath[] = triangle[][];
for ( int i=; i<triangle.size(); ++i )
{
for ( int j=triangle[i].size()-; j>; --j )
{
minPath[j] = min(minPath[j-], minPath[j]) + triangle[i][j];
}
minPath[] += triangle[i][];
}
int ret = minPath[];
for ( int i=; i<minPath.size(); ++i ) ret = min(ret, minPath[i]);
return ret; }
};
【Triangle 】cpp的更多相关文章
- 【Pascal's Triangle】cpp
题目: Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,R ...
- leetcode 【 Triangle 】python 实现
题目: Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjace ...
- 【Permutations】cpp
题目: Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the fo ...
- 【Subsets】cpp
题目: Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset ...
- 【Anagrams】 cpp
题目: Given an array of strings, return all groups of strings that are anagrams. Note: All inputs will ...
- 蓝桥杯 【dp?】.cpp
题意: 给出一个2*n的方格,当刷完某一个方格的漆后可以且只可以走到相邻的任何一格,即上 下 左 右 左上 左下 右上 右下.可以从任意一个格子开始刷墙,问有多少种刷法,因为随着n的增大方案数会变多, ...
- 【N-Queens】cpp
题目: The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two que ...
- 【Combinations】cpp
题目: Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. For ex ...
- 【Candy】cpp
题目: There are N children standing in a line. Each child is assigned a rating value. You are giving c ...
随机推荐
- 360 so动态脱壳
环境及工具 手机 : 中兴 U887 系统版本: Android 2.3.5 工具 : IDA pro 6.6 .0101Editor 版权声明:未经许可,随便转载 目前so加壳有 ...
- URL最大长度问题
在http协议中,其实并没有对url长度作出限制,往往url的最大长度和用户浏览器和Web服务器有关,不一样的浏览器,能接受的最大长度往往是不一样的,当然,不一样的Web服务器能够处理的最大长度的UR ...
- Masonry 等间隔或等宽高排列多个控件
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...
- SqlServer作业指定目标服务器
用SSMS生成数据库作业的创建脚本的时候,有一步是sp_add_jobserver操作: EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = ...
- IOS UIView动画(封装动画)
● UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView 将为这些改变提供动画支持 ● 执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视 图, ...
- 2017.12.25 Java中面向对象思想的深刻理解
今日内容介绍 1.面向对象思想 2.类与对象的关系 3.局部变量和成员变量的关系 4.封装思想 5.private,this关键字 6.随机点名器 01面向对象和面向过程的思想 * A: 面向过程与面 ...
- 说说qwerty、dvorak、colemak三种键盘布局
[qwerty布局] qwerty布局大家应该都很熟悉了,全世界最普及的键盘布局. 截止到去年接触并使用dvorak布局之前,我使用了十几年qwerty布局,在http://speedtest.10f ...
- unsigned __int64 打印方法
原文出处 long 和 int 范围是[-2^31,2^31),即-2147483648~2147483647. 而unsigned范围是[0,2^32),即0~4294967295.也就是说,常规的 ...
- 在Drupal7中创建web接口
Services 模块允许您从一个主要模块后端配置和管理区域启用您 Drupal 站点上自定义构建的内容服务器和服务.该模块中包含的服务允许您调用内容,从 Drupal 的默认和分配的 File.Co ...
- java基础编程——获取栈中的最小元素
题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1)). 题目代码 /** * Created by YuKai Fan on 2018/9 ...