力扣 | 递增子序列 | 动态规划 | 最长递增子序列、最长递增子序列的个数、及其变式

news2024/9/23 5:23:59

文章目录

  • 一、300. 最长递增子序列
  • 二、673. 最长递增子序列的个数
  • 三、变式
    • 1、646. 最长数对链
    • 2、1218. 最长定差子序列
    • 3、1027. 最长等差数列
    • 4、354. 俄罗斯套娃信封问题
    • 5、1964. 找出到每个位置为止最长的有效障碍赛跑路线
  • 四、2024复旦计科夏令营机试

最长递增子序列:原序-递增数值问题
最长定差子序列:原序-定差数值问题
最长数对链:非原序-递增区间问题
俄罗斯信封套娃:非原序-递增二维数值问题

一、300. 最长递增子序列

LeetCode:300. 最长递增子序列
在这里插入图片描述
一个很容易的解法,就是定义 d p [ i ] dp[i] dp[i]表示以第 i + 1 i+1 i+1个数字结尾的最长递增子序列长度,要求出这个长度,只需要往前遍历一次,看看前面有没有比它小的字符,这样就可以构成一个递增子序列。那么对于第 i + 1 i+1 i+1个字符而言,将它之前的所有数字遍历一遍可以求出最长递增子序列。
时间复杂度: O ( n 2 ) O(n^2) O(n2)
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(), 1);
        int ans = 1;
        for(int i = 1; i < nums.size(); ++ i){
            for(int j = i - 1; j >= 0; -- j){
                if(nums[i] > nums[j]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    ans = max(ans, dp[i]);
                }
            }
        }
        return ans;
    }
};

不过我们知道要求出 d p [ i ] dp[i] dp[i]只需要找到前面比它小的数字即可。

我们可以动态维护一个最长递增序列,这样序列是这样的, l i s t [ i ] list[i] list[i]表示当前长度为 i + 1 i+1 i+1的递增子序列的末尾的最小数字。我们可以动态维护这样的数据结构。

  • 当我们考虑 d p [ i ] dp[i] dp[i]时,我们可以通过 l i s t list list来快速求出 d p [ i ] dp[i] dp[i]的值(二分查找)。因为 l i s t list list是按递增子序列长度排列的,并且每一个长度都维护了一个最小值。
  • 对于第 i + 1 i+1 i+1个数字,看它的递增长度 l e n len len 来与 l i s t [ l e n ] list[len] list[len]对比,维护 l i s t list list

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> lst;
        for(int i = 0; i < nums.size(); ++ i){
            int left = 0, right = (int) lst.size() - 1, mid;
            while(left <= right){//二分查找,查找小于nums[i]的最大值
                mid = (right - left) / 2 + left;
                if(lst[mid] >= nums[i]){
                    right = mid - 1;
                }else left = mid + 1;
            }
            if(left == lst.size()) lst.emplace_back(nums[i]);
            if(lst[left] > nums[i]) lst[left] = nums[i];
        }
        return lst.size();
    }
};

二、673. 最长递增子序列的个数

LeetCode:673. 最长递增子序列的个数
在这里插入图片描述
这个题难度大很多!我们无法直接使用二分查找!因为我们维护的递增子序列是最小值!即使记录每个位置现在的个数也无法进行状态转移呀! 比如[1, 5 , 2 , 3],这里我们维护了[1, 5]作为长度为2的,现在我们加入2,现在长度为2变成了[1,2],但我们并不能说长度为2的有两个,这样加入3的时候,一共有两个长度为3的序列,[1,2,3]。原因就在于我们维护的递增子序列每次都是最小的,后面加入的更长并不一定前面更小的都能和他构成更长的

那我们先考虑直接暴力计算的方法吧!

我们不构造,直接二维循环。我们仍然使用 d p [ i ] dp[i] dp[i]表示第 i + 1 i+1 i+1个数字为尾部的最长递增子序列;我们使用 c n t [ i ] cnt[i] cnt[i]表示第 i + 1 i+1 i+1个数字为尾部的最长递增子序列的个数。
时间复杂度: O ( n 2 ) O(n^2) O(n2)

class Solution {
public:
    int findNumberOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(), 1);
        vector<int> cnt(nums.size(), 1);
        int mx = 1;
        int ans = 0;
        for(int i = 0; i < nums.size(); ++ i){
            for(int j = i - 1; j >= 0; -- j){
                if(nums[i] > nums[j]){
                    if(dp[j] + 1 > dp[i]){
                        dp[i] = dp[j] + 1;
                        cnt[i] = cnt[j];
                    }else{
                        if(dp[j] + 1 == dp[i]){
                            cnt[i] += cnt[j];
                        }
                    }
                }
            }
            if(dp[i] == mx){
                ans += cnt[i];
            }else{
                if(dp[i] > mx) mx = dp[i], ans = cnt[i];
            }
        }
        return ans;
    }
};

高阶思路:
对于300. 最长递增子序列的解法二,我们维护的一个标准的最小值。

如果我们能想到另外一点就太强了:

  • 我们每次去替换 l i s t [ i ] list[i] list[i]实际上都是当前值 v a l val val l i s t [ i ] list[i] list[i]小,那么我们换一个想法,我们不去替换它,而是将 l i s t [ i ] list[i] list[i]扩展为一个数组,想替换最小值变为它加在这个数组的末尾,这样我们就能够保证,每次加入一个 v a l val val都能保证 l i s t [ i ] list[i] list[i]是单调非增的。而且跟我们不看数组的所有元素,只看最末尾的元素的话,实际上和300. 最长递增子序列一样,所以我们能够保证每次加入的是正确的位置。
  • 这样做,我们就知道,我们考虑任何一个数 v a l val val时,它如果放在 l i s t [ i ] list[i] list[i]处,我们能够知道它能构成多少个长度为 i + 1 i+1 i+1的递增子序列,这个数量这跟 l i s t [ i − 1 ] list[i-1] list[i1]有关,而且在 l i s t [ i − 1 ] list[i-1] list[i1]的数都出现在 v a l val val之前,所以一定是可以进行状态转移的。

在这里插入图片描述

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

class Solution {
    int binarySearch(int n, function<bool(int)> f) {
        int l = 0, r = n;
        while (l < r) {
            int mid = (l + r) / 2;
            if (f(mid)) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        return l;
    }

public:
    int findNumberOfLIS(vector<int> &nums) {
        vector<vector<int>> d, cnt;
        for (int v : nums) {
            int i = binarySearch(d.size(), [&](int i) { return d[i].back() >= v; });
            int c = 1;
            if (i > 0) {
                int k = binarySearch(d[i - 1].size(), [&](int k) { return d[i - 1][k] < v; });
                c = cnt[i - 1].back() - cnt[i - 1][k];
            }
            if (i == d.size()) {
                d.push_back({v});
                cnt.push_back({0, c});
            } else {
                d[i].push_back(v);
                cnt[i].push_back(cnt[i].back() + c);
            }
        }
        return cnt.back().back();
    }
};

以下问题我们只谈他们使用最长递增子序列的方式解决的方法。这里并不是说要你记住这个方法,而是理解他们为什么能这样用,扩展思维。

三、变式

1、646. 最长数对链

LeetCode:646. 最长数对链
在这里插入图片描述

这个问题最快速和方便是使用贪心算法,不过我们也可以线排序 然后像最长递增子序列一样,使用时间复杂度为 O ( n 2 ) O(n^2) O(n2)的动态规划。
在这里插入图片描述

贪心算法的证明:

对于任何一个数对链,首先对于任何一个维度,都是递增的;其次它构成的链也是递增的。我们来考察数对链中的其中任何一个数对,如果我们能找到满足要求的更小的数对(任何一维更小或者两维都能小),那么我可以将其替换。根据这个原因,我们可以将第二维排序,另一维按最小能满足条件的值来进行选择。
实际上将其映射到数轴上,可以发现我们最好选择两端都更小的且满足要求的,这样不会因为右端更大而导致有一些重叠不能选。这也是为什么我们每次都选择最小,而不选择一端大一端更小的。

2、1218. 最长定差子序列

1218. 最长定差子序列
在这里插入图片描述
这个题也一样直接像最长递增子序列一样,使用时间复杂度为 O ( n 2 ) O(n^2) O(n2)的动态规划。

当然为什么最长对数链需要排序,而他们不要? 因为他们求的是子序列顺序已经固定了。而最长对数链有两维,必须满足其中一维满足要求才能用这种动态规划方式。

但是不得不说这个题和最长递增子序列的区别是什么:

  • 最长递增子序列的关系是增,而这里的关系是差difference
  • 确定一个数val,无法在最长递增子序列中确定它是哪一个,而这里可以确定它的前驱val - difference。这样就可以引入哈希查找了!将 O ( n 2 ) O(n^2) O(n2)降为 O ( n ) O(n) O(n)
class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) {
        unordered_map<int, int> mp;//以mp[i]结尾的最长递增子序列
        int mx = 1;
        for(int i = 0; i < arr.size(); ++ i){
            if(mp.count(arr[i] - difference) == 0){
                mp[arr[i]] = 1;
            }else{
                mp[arr[i]] = mp[arr[i] - difference] + 1;
            }
            mx = max(mx, mp[arr[i]]);
        }

        return mx;
    }
};

由于数字就那么大,使用数组进行存储比哈希表更快。但是我们需要注意一些问题:
在这里插入图片描述

  • − 1 0 4 < = a r r [ i ] , d i f f e r e n c e < = 1 0 4 -10^4 <= arr[i], difference <= 10^4 104<=arr[i],difference<=104,那么将其映射到正数里是 [ 0 , 2 × 1 0 4 ] [0,2×10^4] [0,2×104],千万别认为是 [ 0 , 1 0 8 ] [0,10^8] [0,108],两边同时加上 1 0 4 10^4 104,是两倍关系不是指数关系。
  • 注意 a r r [ i ] − d i f f e r e n c e arr[i] - difference arr[i]difference的取值范围是: [ − 2 × 1 0 4 , 2 × 1 0 4 ] [-2×10^4,2×10^4] [2×104,2×104],所以得开的数组范围是 [ 0 , 4 × 1 0 4 ] [0,4×10^4] [0,4×104]
class Solution {
public:
    int longestSubsequence(vector<int>& arr, int difference) {
        int mx = 1;
        array<int, 40000 + 1> mp = {};

        for(int i = 0; i < arr.size(); ++ i){
            mp[arr[i] + 20000] = mp[arr[i] - difference + 20000] + 1;
            mx = max(mx, mp[arr[i] + 20000]);
        }

        return mx;
    }
};

3、1027. 最长等差数列

LeetCode:1027. 最长等差数列
在这里插入图片描述
这一题和之前的区别在于,等差的大小是不知道的,我们不能拿着一个数就确定我们需要的他的最长等差长度,因为这跟等差的大小有关,因此我们定义 d p [ i ] [ d ] dp[i][d] dp[i][d]表示以i结尾的等差为d的最长长度,我们只需要遍历d就能像最长定差子序列一样求解问题了。

不过我们需要注意两个问题:

  • 定义array时需要特别注意顺序,二维越界不一定会出错,容易混淆顺序
  • 等差有正的有负的,因此等差的范围为 [ − 500 , 500 ] [-500,500] [500,500],并且当一个数没有这样的等差值时,它自己当做等差数列,因此长度为1。
class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        array<array<int, 1001>, 501> dp = {};//注意定义的顺序,不如直接int dp[501][1001];
        int mx = 1;

        for(int i = 0; i < nums.size(); ++ i){
            for(int d = -500; d <= 500; ++ d){//等差的值
                if(nums[i] - d < 0 || nums[i] - d > 500) dp[nums[i]][d + 500] = 1;//当一个数没有这样的等差值时,它自己当做等差数列,因此长度为1
                else dp[nums[i]][d + 500] = dp[nums[i] - d][d + 500] + 1;

                mx = max(dp[nums[i]][d + 500], mx);
            }
        }

        return mx;
    }
};

4、354. 俄罗斯套娃信封问题

LeetCode:354. 俄罗斯套娃信封问题
在这里插入图片描述
乍一看这个题目好像跟LeetCode:646. 最长数对链一毛一样,最长对数链使用贪心能够解决,难道这道题也可以?答案是不可以,原因在于,信封一端更小,但另一端可能很大导致其他信封塞不下。

最长对数链之所以右端更小就更好的原因在于,它不会影响比它大的数对的选择,选择更大的可能这个更大的区间中间可以放多个,选择右端最小就不会有这样的问题 即右端优先。但信封任何一端都没有这样的优先权,两端是等价的。

这个题和数对链一样,它们不是子序列的问题,直接二重循环是不行的。我们必须知道本质,能变成子序列问题是因为,答案必然由“子序列”构成,而不能是其他的顺序。

那我们如果使用二重循环,那就需要保证问题的解是子序列,我们需要从答案中考虑:

  • 答案保证了wh是单增的,那么我们在数组中尝试让w单增,那么选择时 答案是否就是这个子序列了呢?(很难想到吧)
  • 官解告诉我们,并不是,原因在于当w存在相等的值时,我们仅仅看h的大小会有问题,实际上这些信封都不能套起来,因此我们需要保证w相同时,不会导致只考虑h出现问题。这里有两种方式:
    • ①保证w相同时,h是降序,这样单考虑h构成的子序列不会出现把相同的宽度套在一起的情况。
    • ②我们再来考虑一下w,相同w的信封不被套起来。

信封套娃实际上可以转化为二维最长递增子序列的问题。
我们这样就发现了问题的本质:
(1)使用从前往后遍历的方式来查看当前值和之前值能构成的答案时,实际上就必须保证解是子序列。无论是维护答案还是二重循环。
(2)从答案中思考问题的结构,答案必须保证升序。因此将二维中的一维升序转换后,实际上答案必然就在转换后的子序列中(原因是答案必然是其中一维升序!这是非常神奇的点)

使用二重循环的方法:实际上将其中一维升序之后,就变成了最长递增子序列的问题。不作第二维处理就剔除一下升序那维相同的情况即可。

使用二分查找的方法:仅仅将其中一维升序不够,因为第二维放的位置是跟大小有关系的,无法做到通过另一维判断情况。那么为了保证一维相同时的情况不会被考虑,我们得对相同时的情况的另一维做处理。 我们要刨除相同一维的影响,直接使得另一维不可能构成答案,应该怎么做? 正确的做法是在保证一维升序的情况下,另一维降序,这样的话单独考虑这些宽度相同的信封时,他们不可能在子序列的情况下构成嵌套关系。 这样的话就一定不会出现导致相同宽度的信封被认为嵌套。

方法一: 二重循环
时间复杂度: O ( n 2 ) O(n^2) O(n2) 超时

不用二维降序,多加一个判断就好:

bool cmp(vector<int> & a, vector<int> & b){
    return a[0] < b[0];
}
class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), cmp);
        
        vector<int> dp(envelopes.size(), 1);
        int mx = 1;

        for(int i = 1; i < envelopes.size(); ++ i){
            for(int j = i - 1; j >= 0; -- j){
                if(envelopes[i][0] != envelopes[j][0] && envelopes[i][1] > envelopes[j][1]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    mx = max(mx, dp[i]);
                }
            }
        }

        return mx;
    }
};

使用二维降序,直接就变成了最长递增子序列的问题:

bool cmp(vector<int> & a, vector<int> & b){
    if(a[0] == b[0]) return a[1] > b[1];
    return a[0] < b[0];
}
class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), cmp);
        
        vector<int> dp(envelopes.size(), 1);
        int mx = 1;

        for(int i = 1; i < envelopes.size(); ++ i){
            for(int j = i - 1; j >= 0; -- j){
                if(envelopes[i][1] > envelopes[j][1]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    mx = max(mx, dp[i]);
                }
            }
        }

        return mx;
    }
};

方法二: 启发式排序 + 二分查找
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

bool cmp(vector<int> & a, vector<int> & b){
    if(a[0] == b[0]) return a[1] > b[1];
    return a[0] < b[0];
}
class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), cmp);
        int n = envelopes.size();

        vector<int> lst;

        for(int i = 0; i < n; ++ i){
            auto it = lower_bound(lst.begin(), lst.end(), envelopes[i][1]);//寻找大于等于的第一个位置,不要用upper因为等于也是不行的
            if(it == lst.end()){
                lst.emplace_back(envelopes[i][1]);
            }else{
                *it = min(*it, envelopes[i][1]);
            }
        }

        return (int) lst.size();
    }
};

方法三: 单维排序 + 二分查找
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),虽然运行过慢,但实际上就是 O ( n l o g n ) O(nlogn) O(nlogn),慢的原因是判断的条件多了很多,空间大了一些。
在这里插入图片描述
这种方法官解没有提供(不是想拓展一下思路深度思考的,别看了,判断写的太长了,条件有点多)

还可以观察到一个有意思的事情,由于相同w的信封是按顺序排列的,那我们一定会按顺序进行考虑,我们什么时候会出现错误的情况呢?
我们来考虑在求 最长递增子序列的个数时, 我们维护一个最长递增子序列 并维护每个位置有哪些数。对于相同宽度的信封 e 1 , e 2 , e 3 , e 4 e1,e2,e3,e4 e1,e2,e3,e4(从小到大排序,即 e 1 < e 2 < e 3 < e 4 e1<e2<e3<e4 e1<e2<e3<e4,对于当前维护的信封 m 1 , m 2 , m 3 , m 4 , m 5 m1,m2,m3,m4,m5 m1,m2,m3,m4,m5,如果将 e i ei ei替换 m 1 m1 m1 ~ m 4 m4 m4中的任何一个信封,不管 e i ei ei怎么替换最长长度都是对的,我们可以会替换成 e 1 , e 2 , e 3 , e 4 , m 5 e1,e2,e3,e4,m5 e1,e2,e3,e4,m5,这个时候我们随便举个例子 e 4 e4 e4的位置,代表的是以 e 4 e4 e4结尾的能构成长度为4的套娃!但实际上能行吗? 如果有这样的情况 m 2 < e 3 < e 4 < m 3 < m 4 m2 < e3< e4 <m3 < m4 m2<e3<e4<m3<m4,我们说当 e 3 e3 e3替换掉 m 3 m3 m3之后, e 4 e4 e4被认为放到 m 4 m4 m4的位置,这是对的嘛?显然是不对的, e 4 e4 e4根本无法构成更长的,所以出现错误。
如果我们同样来维护每个位置有哪些信封,这个时候你必须用 e 4 e4 e4 e 3 e3 e3的位置里的所有信封进行比较,如果存在一个使得比 e 4 e4 e4更小(实际上只需要比一次),那么 e 4 e4 e4可以替换 m 4 m4 m4,如果不存在,那么 e 4 e4 e4需要被抛弃,因为它的作用不及 e 3 e3 e3
但如果是这样的,我们考虑 m 1 < e 3 < m 2 < m 3 < e 4 < m 4 m1 < e3 < m2 < m3 < e4 < m4 m1<e3<m2<m3<e4<m4,这个时候又是什么情况? m 2 m2 m2一定被 e 3 e3 e3替换, e 4 e4 e4是否一定能替换 m 4 m4 m4?答案是一定的!因为即使没有 e 3 e3 e3的存在, e 4 e4 e4也一定能替换 m 4 m4 m4,有了并不影响,这样我们就可以写出一个新算法!并不需要第二维排序了,多加一个判断就行。(空间复杂度稍微高了一点点)
虽然我们知道有了个新方法,但是别忘了官解最好是第二维降序!这样就不会出现这样的问题了,因为这样第一维相同,第二维不可能构成答案了!

bool cmp(vector<int> & a, vector<int> & b){
    /*if(a[0] == b[0]) return a[1] < b[1];*/
    return a[0] < b[0];
}
class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), cmp);
        vector<vector<vector<int>>> lst;

        for(int i = 0; i < envelopes.size(); ++ i){
            int left = 0, right = (int) lst.size() - 1, mid;
            while(left <= right){
                mid = (right - left) / 2 + left;
                if(lst[mid].back()[1] < envelopes[i][1]){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }

            //right left,left是需要放的地方
            if(right == -1 || lst[right].back()[0] != envelopes[i][0]){//与前一个位置的最后一个信封宽度不相同:直接放就行
                if(left == lst.size()){
                    lst.push_back({envelopes[i]});
                }else {
                    if(lst[left].back()[0] == envelopes[i][0]){//考虑当前位置的最后一个是否和当前位置最后一个信封宽度相同,相同用小的替代
                        if(lst[left].back()[1] > envelopes[i][1]){
                            lst[left].back() = envelopes[i];
                        }
                    }else lst[left].emplace_back(envelopes[i]);
                }
            }else{//与前一个位置的最后一个信封宽度相同
                if(lst[right].size() >= 2){//前一个位置的信封至少要有两个才行 有一个肯定不能放
                    if(lst[right][(int) lst[right].size() - 2][1] < envelopes[i][1])//有两个,看看是否能放
                    if(left == lst.size()){
                        lst.push_back({envelopes[i]});
                    }else {
                        if(lst[left].back()[0] == envelopes[i][0]){
                            if(lst[left].back()[1] > envelopes[i][1]){
                                lst[left].back() = envelopes[i];
                            }
                        }else lst[left].emplace_back(envelopes[i]);
                    }
                }
            }
        }

        return lst.size();
    }
};

5、1964. 找出到每个位置为止最长的有效障碍赛跑路线

这个题实际上就是维护最长来使用二分查找的最长递增子序列的问题。唯一区别就是可以相同,相当于最长非减子序列。

class Solution {
public:
    vector<int> longestObstacleCourseAtEachPosition(vector<int>& obstacles) {
        vector<int> lst;
        vector<int> ans;
        int n = obstacles.size();

        for(int i = 0; i < n; ++ i){
            auto it = upper_bound(lst.begin(), lst.end(), obstacles[i]);
            if(it == lst.end()){
                lst.emplace_back(obstacles[i]);
                ans.emplace_back(lst.size());
            }else{
                *it = min(*it, obstacles[i]);
                ans.emplace_back(it - lst.begin() + 1);
            }
        }

        return ans;
    }
};

四、2024复旦计科夏令营机试

动态最长递增子序列
给定m个操作op 1 < = m < = 1 0 5 1<=m<=10^5 1<=m<=105),当op=1时,向数组中插入给定元素val;当op=2时,向数组中弹出一个元素。请给出每次操作后的最长递增子序列长度。

输入样例:
5
1 2
1 5
2
1 7
1 1
输出样例:
1
2
1
2
1

待更新解法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041882.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker容器逃逸

Docker容器逃逸 Privileged 特权模式容器逃逸 | T Wiki (teamssix.com)https://wiki.teamssix.com/cloudnative/docker/docker-privileged-escape.html 1、云原生-Docker 安全-容器逃逸&特权模式 2、云原生-Docker 安全-容器逃逸&挂载 Procfs 3、云原生-Docker 安…

Linux云计算 |【第二阶段】OPERATION-DAY3

主要内容&#xff1a; Nginx调度器&#xff08;7层代理服务器Http、Nginx&#xff0c;4层代理服务器SSH&#xff09;、配置upstream服务器集群池属性&#xff0c;HTTP错误代码&#xff0c;Nginx优化&#xff08;自定义404错误代码、状态页面显示、ab压力测试、客户端开启缓存、…

在HMI项目中,传感器扮演的角色是啥?一文告诉你。

说到HMI项目&#xff0c;就绕不开物联网&#xff0c;说到物联网就不得不说传感器&#xff0c;本文大千UI工场带你详细了解传感器的价值。 一、传感器的价值 在HMI&#xff08;Human-Machine Interface&#xff09;项目中&#xff0c;传感器扮演着收集和监测实时数据的角色。传…

Tire树-存储与查找

#include <iostream>using namespace std;const int N 100010; // 定义常量 N 表示字典树节点的最大数量int son[N][26], cnt[N], idx; // son数组存储字典树&#xff0c;cnt数组记录某个字符串结束时的节点个数&#xff0c;idx表示当前字典树的节点总数 char str[N];…

数据结构之----堆

一、介绍 堆是一棵完全二叉树。堆又分为大堆&#xff0c;小堆两种结构。 大堆&#xff1a;所有的父节点都比它的子节点要大。 小堆&#xff1a;所有的父节点都比它的子节点要小。 二、堆的向上调整算法 比如要建一个小堆 思路&#xff1a;将父节点和子节点比较&#xff0c…

驰骋BPM RunSQL_Init SQL注入漏洞复现

0x01 产品简介 驰骋BPM系统由济南驰骋信息技术有限公司研发,具有悠久的历史和丰富的行业经验。其工作流引擎CCFlow自2003年开始研发,是国内知名的老牌工作流引擎,在BPM领域拥有广泛的研究群体与应用客户群。统提供.net与java两个版本,且两个版本的代码结构、数据库结构、设…

手写数字识别实战

全部代码&#xff1a; import matplotlib.pyplot import torch from torch import nn # nn是完成神经网络相关的一些工作 from torch.nn import functional as F # functional是常用的一些函数 from torch import optim # 优化的工具包import torchvision from matplotlib …

简单回归问题实战

数据表&#xff1a;链接: https://pan.baidu.com/s/1sSz7F_yf_JeumXcP4EjE5g?pwd753f 提取码: 753f 核心流程&#xff1a; import numpy as np # 计算误差函数 points是数据集中数据的位置 def compute_error_for_line_given_points(b,w,points):totalError0for i in range(0…

【FreeRTOS】队列的本质

目录 0 前言1. 数据传输的方法1.1 任务之间如何传输数据1.2 队列的本质1.3 操作队列的三个步骤 2 队列2.1 举例说明2.2 唤醒流程2.2.1 情况12.2.2 情况2 3 总结 0 前言 学习视频&#xff1a; 【FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS&#xff08;FreeRTOS教程 基…

Haproxy基于cookie的会话保持

cookie value&#xff1a;为当前server指定cookie值&#xff0c;实现基于cookie的会话黏性&#xff0c;相对于基于 source 地址hash 调度算法对客户端的粒度更精准&#xff0c;但同时也加大了haproxy负载&#xff0c;目前此模式使用较少&#xff0c; 已经被session 共享服务器代…

亚信安慧AntDB-M聚合下推—加速你的数据分析查询

摘 要 在业务系统中&#xff0c;一般的事务型SQL语句涉及到的数据记录数不会很多&#xff0c;即便涉及到多个数据节点&#xff0c;基于AntDB-M的优化&#xff0c;访问也都很快。但是统计分析型SQL语句往往涉及到大量数据&#xff0c;甚至包括全表数据&#xff0c;基本都会覆盖…

3D 技术对我们的生活有哪些影响?

3D技术&#xff0c;也称为三维技术&#xff0c;是指利用计算机生成或处理三维数据的技术。它在多个领域对我们的生活产生了深远的影响&#xff1a; 1、制造业&#xff1a;3D技术使得个性化和定制化生产成为可能&#xff0c;大幅缩短了产品从设计到制造的时间&#xff0c;降低了…

【人工智能】Transformers之Pipeline(十):视频分类(video-classification)

目录 一、引言 二、视频分类&#xff08;video-classification&#xff09; 2.1 概述 2.2 技术原理 2.3 应用场景 2.4 pipeline参数 2.4.1 pipeline对象实例化参数 2.4.2 pipeline对象使用参数 2.4 pipeline实战 2.5 模型排名 三、总结 一、引言 pipeline&#x…

网络编程 8/15 基于UDP多人聊天室

//客户端代码 #include <myhead.h> struct msgType {char type; // 消息类型L:登录&#xff0c;Q:退出&#xff0c;C:聊天char usrName[20];char msgText[1024]; }; #define SER_PORT 6666 // 服务器端口 #define SER_IP "192.168.2.161" // 服务器IP…

SpringBoot解决创建项目无法选择JDK8和JDK11

文章目录 解决方案1解决方案2 在创建SpringBoot项目的时候&#xff0c;我们发现只能勾选JDK17以上的。并且官方没有提供2.X版本&#xff0c;但是目前大多数企业使用的还是 springboot 初始化的网址&#xff0c;我们一般使用的是官方的网址。 解决方案1 就选择jdk17和spring…

BIM+GIS在管廊机电监控与运维管控系统中的应用

研究背景 根据《GB50838-2015城市综合管廊工程技术规范》及《GBT51274-2017城镇综合管廊监控与报警系统工程技术标准》的相关条款要求&#xff0c;城市综合管廊监控报警系统用于对综合管廊内的设备运行状态及参数、实时环境信息、出入口状态等进行全方位在线监控&#xff0c;保…

java基础概念17-static

一、 static的作用 static修饰的变量、方法被类的所有实例共享。 示例&#xff1a; static用于声明属于类本身的变量、方法&#xff0c;而不是类的某个特定对象的。 二、static内存图 静态区中的成员变量&#xff0c;对象共享&#xff0c;内存中只有一份&#xff0c;谁要用&am…

u2net 和u2netp 的具体区别

U2Net和U2NetP是两种基于深度学习的图像分割模型&#xff0c;它们都使用了编码器-解码器架构和跳跃连接来提高分割的精度。然而&#xff0c;它们在网络结构和参数配置上存在一些差异。 初始化阶段的中间通道数 (mid_ch): U2Net: self.stage1 RSU7(in_ch, 32, 64)U2NetP: self.…

RHEL8 配置epel源

** RHEL8 配置epel源 ** 此次环境为最小化安装&#xff0c;版本信息如下&#xff1a;redhat8 一、安装epel源&#xff0c;执行如下命令&#xff1a; #yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm 之后执行#yum repolis 此时我们…

山海关古城测试--房产房屋

1.树状图搜索房产 1.1实现思路 因为树状图搜索要显示在界面中&#xff0c;所以需要在html文件中进行添加树状结构 而树状结构怎么来的&#xff0c;所以需要在controller中新写一个方法来传输一个树状结构&#xff0c;让html直接访问它即可树状结构在framework->web->Zt…