动态规划 - 堆箱子 - 困难 - 再尝试

*************

C++

TOPIC: 面试题 08.13. 堆箱子 - 力扣(LeetCode)

*************

再简单的看一眼困难的题目,

依然是比较困难的,我直接去看一下之前的困难的题目的核心方法:

不同子序列:需要考虑之前已经匹配过得,核心就是加法。

不适用于这道题目,经过昨天的思考,已经知道了这个核心就是排序,需要同时满足长宽高的严格递增或者严格递减顺序。

想到这,就觉得之前看过的300. 最长递增子序列 - 力扣(LeetCode),这个我还没有做,正好,来都来了,现在就给他做了吧,做一困难的,送一中等难度的,赚了。

简单地看一眼中等难度的题目:

根据经验,暴力美学不行,会超时,the aesthetics of violent还是过于优雅了。根据经验,这个起手就是动态规划而不是哈希表。为什么?因为我会看一眼标签:

整理一下思路,dp[i]代表至第 i 位,严格递增子序列的长度。这个就跟小学时候解方程一样,题目问什么,就设什么是 X 一样。

还是根据经验,暴力美学会超时:

对于每一个i,遍历之前的所有数字,即 j 从0 ~ i - 1,如果nums[i] > nums[j],那么dp[i] = max(dp[i], dp[j] + 1)。

数组的起手就是 s.size();并且初始化为1,为什么是1而不是0呢?因为即使是1个数字,也是满足题目要求的最长严格递增子序列。

然后需要两个循环:

外层循环 int i = 0; i < n; i++; // go through all the string

内层循环 int j = 0; j < i; j++; // go through all the elements before i

接着判断:

if (nums[i] > nums[j]) return (dp[i] = max(dp[i], dp[j] + 1);

手搓代码完成,行得通,先写试一试。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size(); // as before
        vector<int> dp(n, 1); // initialise the dp

        if (n == 0) return 0;

        for (int i = 0; i < n; i++){
            for (int j = 0; j < i; j++){
                if (nums[i] > nums[j]){
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
        }

        int max_len = *max_element(dp.begin(), dp.end());
        return max_len;
    }
};

竟然没超时,真是天大的好消息。

 

既然这样的话,我排序3次应该可以。

首先,对箱子进行排序,按照宽度升序,如果宽度相同,再按深度升序,最后按高度升序。 这样可以确保宽度和深度都是非递减的,但是高度可能有波动。 然后,遍历每个箱子i,从0到i-1,检查是否存在箱子j,使得 width[j] < width[i], depth[j] < depth[i], height[j] < height[i] 如果存在,那么 dp[i] = max(dp[j] + height[i]),其中j满足上述条件 如果不存在,那么 dp[i] = height[i] 最后,答案就是dp数组中的最大值。

今天得学一个新的库,sort函数,功能就是排序。

复习一下之前的函数。unordered库,哈希表用到的,用于快速删除啥的。

*************

周四了,今天天气很好,设个大晴天,微风,比较冷,周末想去迪卡侬买两件摇粒绒,天冷了。

还有一件事,就是来了一个老外同事,那个身材真的是没的说,但是,闻着就不便宜的香水,最开始还算OK,但是后调就混合着浓郁的体臭,特别是空调一开,外套一脱,浓郁持久,闻久了容易头晕,真的是原地去世。

回到题目,

初步思路已经整理出来了:

初始化dp数组,大小为n,全部设为0。

然后,遍历每个i从0到n-1:

dp[i] = height[i]

for j from 0 to i-1:

if width[j] < width[i] and depth[j] < depth[i] and height[j] < height[i]:

dp[i] = max(dp[i], dp[j] + height[i]),

最后 return dp[n];

需要注意的是,这里有一个高级语法,也算不上多高级的语法,只不过我不会,我不会的语法统一称为高级语法,等我学会了以后,会很有成就感。

首先就是题目中给了 [width, depth, height],

那么就用:

box[i][0] 表示第 i 个盒子的宽度;

box[i][1] 表示第 i 个盒子的深度;

box[i][2] 表示第 i 个盒子的高度;

最难学的语来了,会有点困难,但是我不怕。

首先,我需要知道一些符号的意思:

!= 是一个比较运算符,用于比较两个值是否不相等。如果两个值不相同,表达式的结果为 true;如果两个值相同,结果为 false

|| 是逻辑或(logical OR)运算符。它用于连接两个布尔表达式,如果至少有一个表达式为 true,则整个表达式的值为 true;只有当两个表达式都为 false 时,整个表达式的值才为 false

&& 是逻辑与(logical AND)运算符。它用于连接两个布尔表达式,只有当两个表达式都为 true 时,整个表达式的值才为 true;如果至少有一个表达式为 false,则整个表达式的值为 false

OK,这个题目的重点我已经掌握了90%了。

再一次开始尝试写代码,

class Solution {
public:
    int pileBox(vector<vector<int>>& box) {
        if (box.empty()) return 0;
        
        // order by 0 1 2, which is width depth and height
        sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });
        
        
    }
};

这段排序代码是抄的,仍然花了 很长时间才能看懂这段代码。

sort:标准库的排序函数,上一段内容已经学会。这里顺便学会了一个标准格式:

void sort(RandomAccessIterator first,
          RandomAccessIterator last, Compare comp);

sort后面有三个元素,第一个是序列开始,第二个是序列结束,第三个是比较条件。

到这里都很好理解,计算机不明白,因此需要告诉计算机,从1到13进行比较。

更难一点的来了,就是这个比较运算的规则怎么让计算机知道呢?

再学一个高级语法,匿名函数。λ 这个高中经常用的到希腊字母,我第一次知道她的英文名字,lambda, b is silent. 所以,Lambda 表达式(也称为匿名函数)是 C++11 引入的一种方便的语法,允许你在需要函数的地方直接编写函数,而不需要提前定义函数。Lambda 表达式通常用于简短的操作,尤其是在需要提供函数作为参数的场合,如排序、查找等算法。一般表达式是:

[capture](parameters) -> return-type { function-body }

// [capture] 可选,定义了 lambda 表达式可以访问的外部变量。可以是值捕获(如 a 或 &a),也可以是列表形式(如 [x, &y])。
// &:通过引用捕获。
// =:通过值捕获。
// [this]:捕获当前对象的指针。

// (parameters) 可选,定义了匿名函数的表达式参数
// -> return-type 可选,定义了表达式返回类型
// { function-body } 现场编写的函数表达式

现在可以非常详细的解释一下抄到的代码的意思了

sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            // sort means using the function
            // box.begin() and box.end() means where is start and where is end
            // [] tell lanbda can visit the outside data
            // const means the data comes second cannot be changed
            // capture by reference
            

box.begin() 定义开始的位置;

box.end() 定义结束的位置;

[] 固定格式,

可选。定义了 lambda 表达式可以访问的外部变量。可以是值捕获(如 a&a),也可以是列表形式(如 [x, &y])。

  • &:通过引用捕获。
  • =:通过值捕获。
  • [this]:捕获当前对象的指针。

const,我觉得是constant的缩写,常量,恒定不变的,这个意味着后面引用的值不能被改变。

const vector<int>& a, const vector<int>& b 是引用 a b 两个值。

在看别人进行比较的函数的时候,我有个点一直不明白,先看代码:

 sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });

这里为什么 a[0] != b[0] 就直接输出 a[0] < b[0]呢,这里哪里做了比较呢?根本就没有比较的过程,不相等就可以直接进行比较吗?

我行明白了,这个是函数过程,不是函数结果,这个代码实现的功能就是,

如果宽度不一样,宽度小的那个排在前面。

整体的函数实现的功能就是:

- 比较宽度,如果宽度不同,直接按宽度升序排列。

- 如果宽度相同,比较深度,按深度升序排列。

- 如果宽度和深度都相同,比较高度,按高度升序排列。

最重要的比较函数写明白了,就可以让箱子排列了。

首先就是知道一共有多少个箱子 box.size();

其次就是初始化一个数组

class Solution {
public:
    int pileBox(vector<vector<int>>& box) {
        if (box.empty()) return 0;
        
        // order by 0 1 2, which is width depth and height
        sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            // sort means using the function
            // box.begin() and box.end() means where is start and where is end
            // [] tell lanbda can visit the outside data
            // const means the data comes second cannot be changed
            // capture by reference

            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });
        
        
        int n = box.size(); // get the numbers of the boxes
        vector<int> dp(n, 0); // initialise the dp and assign 0
        
      
    }
};

对箱子进行遍历循环,并给高度一个初始值,这个也很简单

class Solution {
public:
    int pileBox(vector<vector<int>>& box) {
        if (box.empty()) return 0;
        
        // order by 0 1 2, which is width depth and height
        sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            // sort means using the function
            // box.begin() and box.end() means where is start and where is end
            // [] tell lanbda can visit the outside data
            // const means the data comes second cannot be changed
            // capture by reference

            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });
        
        
        int n = box.size(); // get the numbers of the boxes
        vector<int> dp(n, 0); // initialise the dp and assign 0
        
        for (int i = 0; i < n; ++i) {
            dp[i] = box[i][2]; // initial height
            
    }
};

内层循环,对排在 i 前面的箱子 j 和 i 进行比较,

class Solution {
public:
    int pileBox(vector<vector<int>>& box) {
        if (box.empty()) return 0;
        
        // order by 0 1 2, which is width depth and height
        sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            // sort means using the function
            // box.begin() and box.end() means where is start and where is end
            // [] tell lanbda can visit the outside data
            // const means the data comes second cannot be changed
            // capture by reference

            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });
        
        
        int n = box.size(); // get the numbers of the boxes
        vector<int> dp(n, 0); // initialise the dp and assign 0
        
        for (int i = 0; i < n; ++i) {
            dp[i] = box[i][2]; // initial height
            for (int j = 0; j < i; ++j) {
                
    }
};

需要同事满足3个条件,box[j][0] < box[i][0] && box[j][1] < box[i][1] && box[j][2] < box[i][2];

那么最大的高度就可以更新为dp[i] = max(dp[i], dp[j] + box[i][2])。

class Solution {
public:
    int pileBox(vector<vector<int>>& box) {
        if (box.empty()) return 0;
        
        // order by 0 1 2, which is width depth and height
        sort(box.begin(), box.end(), [](const vector<int>& a, const vector<int>& b) {
            // sort means using the function
            // box.begin() and box.end() means where is start and where is end
            // [] tell lanbda can visit the outside data
            // const means the data comes second cannot be changed
            // capture by reference

            if (a[0] != b[0]) return a[0] < b[0];
            if (a[1] != b[1]) return a[1] < b[1];
            return a[2] < b[2];
        });
        
        
        int n = box.size(); // get the numbers of the boxes
        vector<int> dp(n, 0); // initialise the dp and assign 0
        
        for (int i = 0; i < n; ++i) {
            dp[i] = box[i][2]; // initial height
            for (int j = 0; j < i; ++j) {
                if (box[j][0] < box[i][0] && box[j][1] < box[i][1] && box[j][2] < box[i][2]) {
                    dp[i] = max(dp[i], dp[j] + box[i][2]);
                }
            }
        }
        
        return *max_element(dp.begin(), dp.end());
    }
};

注意注意,代码是从上往下运行的,因此 for 函数那段,就是对已经排序过的箱子进行筛选。

举个例子:

box 1  = [2, 2, 3],

box 2  = [1, 1, 4],

box 3  = [3, 3, 2]

排序后:

box 2  = [1,1,4],

box 1  = [2,2,3],

box 3  = [3,3,2]

dp=[0,0,0]

i=0:

dp[0]=4

i=1:

dp[1]=3

j=0:

1 < 2, 1 < 2, 4 < 3? 不满足

dp[1]=3

i=2:

dp[2]=2

j=0:

1 < 3, 1 < 3, 4 < 2? 不满足

j=1:

2 < 3, 2 < 3, 3 < 2? 不满足

dp[2]=2

所以,dp=[4,3,2], 返回4。

不出意外的,过两天又会忘记这个代码。忘了那就再学一遍。

have a good day. 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值