每日一题 —— LC. 1687 从仓库到码头运输箱子(难度很大,但值得好好消化的一道题)

news2025/3/10 5:46:00

1687. 从仓库到码头运输箱子

你有一辆货运卡车,你需要用这一辆车把一些箱子从仓库运送到码头。这辆卡车每次运输有 箱子数目的限制总重量的限制

给你一个箱子数组 boxes和三个整数 portsCount, maxBoxesmaxWeight ,其中 b o x e s [ i ] = [ p o r t s i , w e i g h t i ] boxes[i] = [ports_i, weight_i] boxes[i]=[portsi,weighti]

p o r t s i ports_i portsi 表示第 i 个箱子需要送达的码头, w e i g h t s i weights_i weightsi 是第 i 个箱子的重量。
portsCount 是码头的数目。
maxBoxesmaxWeight 分别是卡车每趟运输箱子数目和重量的限制。
箱子需要按照 数组顺序 运输,同时每次运输需要遵循以下步骤:

卡车从 boxes 队列中按顺序取出若干个箱子,但不能违反 maxBoxesmaxWeight 限制。
对于在卡车上的箱子,我们需要 按顺序 处理它们,卡车会通过 一趟行程 将最前面的箱子送到目的地码头并卸货。如果卡车已经在对应的码头,那么不需要 额外行程 ,箱子也会立马被卸货。
卡车上所有箱子都被卸货后,卡车需要 一趟行程 回到仓库,从箱子队列里再取出一些箱子。
卡车在将所有箱子运输并卸货后,最后必须回到仓库。

请你返回将所有箱子送到相应码头的 最少行程 次数。

提示:

  • 1 < = b o x e s . l e n g t h < = 1 0 5 1 <= boxes.length <= 10^5 1<=boxes.length<=105
  • 1 < = p o r t s C o u n t , m a x B o x e s , m a x W e i g h t < = 1 0 5 1 <= portsCount, maxBoxes, maxWeight <= 10^5 1<=portsCount,maxBoxes,maxWeight<=105
  • 1 < = p o r t s i < = p o r t s C o u n t 1 <= ports_i <= portsCount 1<=portsi<=portsCount
  • 1 < = w e i g h t s i < = m a x W e i g h t 1 <= weights_i <= maxWeight 1<=weightsi<=maxWeight

示例

输入:boxes = [[1,2],[3,3],[3,1],[3,1],[2,4]], portsCount = 3, maxBoxes = 3, maxWeight = 6
输出:6
解释:最优策略如下:
- 卡车首先运输第一个箱子,到达码头 1 ,然后回到仓库,总共 2 趟行程。
- 卡车运输第二、第三、第四个箱子,到达码头 3 ,然后回到仓库,总共 2 趟行程。
- 卡车运输第五个箱子,到达码头 2 ,回到仓库,总共 2 趟行程。
总行程数为 2 + 2 + 2 = 6 。

思路

这是本周一(2022/12/5)的每日一题。今天才抽出时间来做。结果一整天就做了这么一道题。脑壳疼。

好在最后看了题解后,把思路想明白了。

这道题,题解中出现的较多的思路是:动态规划+前缀和+单调队列,貌似也可以用动态规划+贪心来做,但自己没走通贪心这条思路,也没找到对应题解。所以下面只记录第一种做法。(2022/12/9,今天再做时,已经把自己的贪心思路完善并提交AC啦!开心)

动态规划+贪心

在记录正确做法之前,还是先记录一下自己的思考过程。

首先这道题,读题+理解题意,都要花比较长的时间。

由于卡车必须按照顺序依次将箱子送到对应的码头,那我先假设一下,如果卡车没有箱子数量和重量的限制,那很明显的,只需要一趟就能完成任务,并且行程次数是固定的。首先把全部箱子拉出去记行程次数+1,然后依次将每个箱子拉到对应码头并卸货,最后返回仓库,记行程次数+1。总的行程次数,就是2(出发和返回),再加上 整个箱子数组,相邻两个箱子为不同的码头的次数。

为了方便叙述,我们设cnt[i, j]表示下标位于[i, j]范围内的所有箱子,相邻两个箱子为不同码头的次数。

现在,由于卡车有数量和重量的限制,一趟出车只能拉有限个箱子。假设某一次出车,拉的箱子的下标范围是[a, b],那么这趟出车的行程次数,容易算得是 2 + cnt[a, b],就是出发和返回各算1次,[a, b]范围内,每一组 相邻,但目标码头不同的箱子,都要算1次行程。

所以,卡车每一趟出车,消耗的行程数就是 2 + cnt[a, b]

假设箱子总共有n个,下标从0开始。若卡车一共出了m趟车,才把所有箱子送到对应的码头,那么消耗的总的行程数,就是

2m + cnt[0, n - 1] ,而cnt[0, n - 1]是一个固定的值,所以很直观的,卡车出车的总趟数,要尽可能的少,才能使得总的行程次数最少。这样来看就有点贪心的味道了。

我们可以把这道题目进行一下抽象,转换为下面这样一个问题:

把整个箱子数组,看成一条线段,我们需要将这条线段,切若干刀,分割成若干个子线段,使得每个子线段都满足某种约束(箱子的数量和重量限制),求解在每个子线段都满足约束的情况下,切分的最少的子线段的数量。(一个子线段就是卡车的一趟出车)

这样来看,对于某个子线段[i, j],我们需要判断其是否满足约束,第一个约束是箱子个数,这可以直接用j - i + 1算得,至于箱子重量,则需要累加整个区间内的箱子重量,容易看出这需要用前缀和来处理。

由于最后计算时,需要计算某个区间内的cnt[i, j]cnt[i, j]也能通过预处理,然后用前缀和来计算。

回到上面说的贪心。我们想要子线段的个数最少,很直观的想法是,要在满足约束的条件下,使得每个子线段的长度尽可能的大。

那么如何使得子线段的长度尽可能的大呢?我们可以枚举子线段的右端点,然后看一下这个右端点,往左最远能够走到什么位置,而不违反限制条件。

我们设i为右端点,设leftBorder[i]为以i作为右端点,往左最远能走到的位置。

由于越往左走,箱子个数越多,箱子总重量越大。所以,从i往左走,如果走到某个位置j,第一次违反了约束条件,那么以i为右端点,往左走最远就只能走到j - 1

并且,对于> i的右端点,其往左走最远不能超过j - 1。可以看到ij的移动,具有单调的性质(向同一个方向移动),所以,求解leftBorder数组,我们可以用一次滑动窗口来完成。

随后,设dp[i]表示,卡车将[0, i]范围内的箱子全部送到目的码头,所需要的最少行程次数。

那么根据上面的贪心思路,我们看最后一趟的情况,让最后一趟运的箱子尽可能的多,设leftBorder[i] = j,则最后一趟运输的箱子的起始位置为j,那么dp[i] = dp[j - 1] + 2 + cnt[j, i]

最后的答案便是dp[n]

根据这个思路写出如下代码

typedef long long LL;
class Solution {
public:
    int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes, int maxWeight) {
        int n = boxes.size();
        
        // vector<LL> preSum(n + 1); // 重量的前缀和
        // for (int i = 1; i <= n; i++) preSum[i] = preSum[i - 1] + boxes[i - 1][1];
        // 这个前缀和好像没什么用? 因为下面已经计算了leftBorder了

        // 需要每个区间内的行程数, 包括区间2个端点, 加上区间内相邻不同数字的和
        vector<int> preSum(n + 1);
        preSum[1] = 0; // n = 1, 2 
        for (int i = 1; i < n; i++) {
            preSum[i + 1] = preSum[i];
            if (boxes[i][0] != boxes[i - 1][0]) preSum[i + 1]++;
        }

        // preSum[i] - preSum[j] 就是[i, j]相邻的但目的码头不同的箱子 的组数 (中间要多加多少次行程)

        vector<int> leftBorder(n + 1); // 某个位置左侧最远能到达的位置
        LL sum = 0;
        for (int i = 1, j = 1; i <= n; i++) {
            sum += boxes[i - 1][1];
            while (sum > maxWeight || i - j + 1 > maxBoxes) {
                sum -= boxes[j - 1][1];
                j++;
            }
            leftBorder[i] = j;
        }


        // 动态规划
        vector<int> dp(n + 1);
        for (int i = 1; i <= n; i++) {
            int l = leftBorder[i]; // 其左侧最远的位置
            dp[i] = dp[l - 1] + 2 + preSum[i] - preSum[l];
        }
        return dp[n];
    }
};

提交后发现只通过了26个样例。失败的样例数据很长,非常不好debug。随后自己找了一组数据,发现上面的贪心做法考虑的不够全面。

比如箱子为:{1,2} {2,3} {3,3} {3,3} {3,3} {4,2} {5,4}maxBoxes = 4maxWeight = 9portsCount = 5

按照我上面贪心的思路,每次转移都是尽可能把卡车装满,那么划分出的子数组情况如下(用|表示切割点):

{1, 2} | {2, 3} {3, 3} {3, 3} | {3, 3} {4, 2} {5, 4}

计算一下此时的行程数:

  • 第一趟出车,{1, 2},只有1个箱子,来回共2次行程
  • 第二趟出车,{2, 3} {3, 3} {3, 3},共3个箱子,来回需要2次行程,中间需要1次行程,共3次
  • 第三趟出车,{3, 3} {4, 2} {5, 4},共3个箱子,来回需要2次行程,中间需要2次行程,共4次

总的行程数为:2 + 3 + 4 = 9

而实际存在一种更优的方案如下:

{1, 2} {2, 3} | {3, 3} {3, 3} {3, 3} | {4, 2} {5, 4}
  • 第一趟出车,{1, 2} {2, 3},共2个箱子,来回行程为2,中间行程1,共3
  • 第二趟出车,{3, 3} {3, 3} {3, 3},共3个箱子,来回2,中间0,共2
  • 第三趟出车,{4, 2} {5, 4},共2个箱子,来回2,中间1,共3

总行程数为:3 + 2 + 3 = 8

为什么会出现这种情况呢?前后两种划分方式,出车的趟数都是三趟,为什么下面的方案总行程数会更少呢?

由于我们需要把整个数组切分成若干段,不妨考虑下切割点的位置。

首先,切割点一定是在数组中间的某两个箱子之间的间隙。我们考虑一下,执行这次切割的前后,对行程数的影响。

我们考虑这个切割点前后两个箱子的情况。假设切割点之前的箱子为x,之后的箱子为y

首先,切割后,一定会比不切割,多出2个行程,即切割点左侧部分的回程,以及切割点右侧部分的去程

其次,我们需要考虑切割点左右两个箱子,xy的目的码头是否相同。

  • xy的目的码头相同,则不切割时,从x走到y消耗的行程为0
  • xy的目的码头不同,则不切割时,从x走到y消耗的行程为1

我们假设,从切割点左侧部分,走到箱子x处,消耗的总行程为dx;从切割点右侧的y,走到末尾,消耗的总行程为dy

  • xy的目的码头相同时
    • 不切割时,总的行程为dx + dy
    • 切割时,总的行程为dx + dy + 2,切割后比切割前,多出的行程数为2
  • xy的目的码头不同时
    • 不切割时,总的行程为dx + dy + 1
    • 切割时,总的行程为dx + dy + 2,切割后比切割前,多出的行程数为1

所以,如果切割的次数相同,则我们需要尽可能的在左右两个箱子的目的码头不等的地方,进行切割,才能使切割导致增加的行程数,尽可能的少。

换句话说,就是如果有某一段连续的箱子,它们目的码头相同,则尽可能的不要从中间切开。

所以我一开始的贪心策略(尽可能把卡车装满)是错误的,或者说是不够完善的。

我们不仅需要考虑尽可能的把卡车装满,还要考虑分割点的位置,将连续某一段目的码头相同的箱子,尽可能放在同一趟去运输。

所以贪心策略应该是二选一。先看尽可能把卡车装满的情况,再考虑分割点的位置,对于连续相同目的码头的箱子,不要切割。二者中取最优。

// C++ 二选一贪心
typedef long long LL;
class Solution {
public:
    int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes, int maxWeight) {
        int n = boxes.size();
        // 需要每个区间内的行程数, 包括区间2个端点, 加上区间内相邻不同数字的和
        vector<int> preSum(n + 1);
        preSum[1] = 0; // n = 1, 2 
        for (int i = 1; i < n; i++) {
            preSum[i + 1] = preSum[i];
            if (boxes[i][0] != boxes[i - 1][0]) preSum[i + 1]++;
        }

        // preSum[i] - preSum[j] 就是[i, j]相邻的但目的码头不同的箱子 的组数 (中间要多加多少次行程)

        vector<int> leftBorder(n + 1); // 某个位置左侧最远能到达的位置
        LL sum = 0;
        for (int i = 1, j = 1; i <= n; i++) {
            sum += boxes[i - 1][1];
            while (sum > maxWeight || i - j + 1 > maxBoxes) {
                sum -= boxes[j - 1][1];
                j++;
            }
            leftBorder[i] = j;
        }

        // left[i]表示, 坐标i的箱子, 左侧的连续相同码头的最左侧的箱子的坐标, right[i]同理
        vector<int> left(n + 1);
        vector<int> right(n + 1);
        int j = 1;
        while (j <= n) {
            int k = j;
            while (k + 1 <= n && boxes[k - 1][0] == boxes[k][0]) k++;
            for (int i = j; i <= k; i++) {
                left[i] = j;
                right[i] = k;
            }
            j = k + 1;
        }

        // 动态规划
        vector<int> dp(n + 1);
        for (int i = 1; i <= n; i++) {
            int l = leftBorder[i]; // 以i为右端点, 左侧最远能到达的位置
            dp[i] = dp[l - 1] + 2 + preSum[i] - preSum[l];
            if (left[l] < l) {
                // 若上面将卡车装满的切割点, 恰好切割在了连续相同码头的箱子中间
                // 则二选一进行转移
                int r = right[l]; // 连续相同码头的最后一个箱子
                // 切在最后一个箱子的右侧
                if (r + 1 <= i) dp[i] = min(dp[i], dp[r] + 2 + preSum[i] - preSum[r + 1]);
            }
        }
        return dp[n];
    }
};

在这里插入图片描述

动态规划+单调队列

下面来看一下单调队列的做法

动态规划的部分和上面一样,设dp[i]表示将[0, i]的全部箱子运输到对应码头,需要的最少行程数量。

考虑状态转移时,还是考虑最后一趟的情况,只不过我们不用贪心策略。我们设最后一趟运输的箱子的范围是[j, i],其中

  • 0 <= j <= i

并且需要满足题目中的2个约束

  • 这一趟的箱子个数不超过maxBoxes

    可以用 i - j + 1 <= maxBoxes 来判断

  • 这一趟的箱子总重量不超过maxWeight

    我们对箱子的重量预处理出前缀和后(假设为weight[i]),则可以用 weight[i] - weight[j - 1] <= maxWeight来判断

对于每一个i,我们可以枚举所有的j ∈ [0, i],来进行状态转移。但是这样的话,总体的时间复杂度就是 O ( n 2 ) O(n^2) O(n2),在这道题目的数据范围下,是会超时的。所以我们需要考虑对状态转移进行优化。

与上面同样的,我们设cnt[i]表示[0, i]区间内,相邻的但目的码头不同的箱子的次数。对于某个区间[j, i]的箱子,我们用transport(j, i)表示运输该区间所有箱子需要的行程次数,那么有transport(j, i) = 2 + cnt[i] - cnt[j],即来回2次行程,加上中间不同目的码头的箱子的次数。

状态转移方程为:dp[i] = dp[j - 1] +transport(j, i) ,即dp[i] = dp[j - 1] + 2 + cnt[i] - cnt[j]

其中需要满足

  • 0 <= j <= i
  • i - j + 1 <= maxBoxes
  • weight[i] - weight[j - 1] <= maxWeight

我们对状态转移方程稍微进行一下变换,由于对于某个状态dp[i],其计算需要枚举全部的j,(j为变量),那么我们将状态转移方程中j的部分放在一起,

dp[i] = dp[j - 1] - cnt[j] + 2 + cnt[i]

最后一项2 + cnt[i],可以看作一个常数c(我们在计算某个dp[i]时,i此时是固定不变的)

那么我们可以把前面关于变量j的部分提取出来,设g[j] = dp[j - 1] - cnt[j]

那么有dp[i] = g[j] + c

我们再观察一下j需要满足的条件,同样对条件不等式进行一下变换,把j放在一边,有

  • j >= i + 1 - maxBoxes
  • weight[j - 1] >= weight[i] - maxWeight

我们考虑两个数,j0 < j1,看下g[j0]g[j1]

  • g[j0] < g[j1],那么dp[i]应该尽可能由更小的g[j0]转移过来。如果j0能满足上面的约束条件,那么由于j1 > j0,则j1更能满足上面的约束条件,则此时应该选择更小的g[j0]进行转移;若j0不能满足上面的约束条件,那么j1 > j0,则j1有可能能满足约束条件,此时应该由g[j1]进行转移。

    所以,在g[j0] < g[j1]的情况下,dp[i]既可能由g[j0]转移过来,也可能由g[j1]转移过来,所以g[j0]g[j1]都需要进行保留

  • g[j0] >= g[j1],那么此时不需要考虑g[j0]了。为什么呢?首先,如果j0能满足约束条件,那么j1一定更能满足约束条件,那么在2个j都满足约束条件的情况下,可以选g[j1],一定不会比选g[j0]更差;如果j0不能满足约束条件,那么j1有可能满足约束条件。所以无论如何,都应该尽可能由g[j1]来进行转移

换句话说,对于某个i,我们准备计算dp[i],则需要枚举所有的j ∈ [0, i],我们需要用到g[j]

对于[0, i]这个区间内的所有j,对于两个相邻的jj0 < j1,若g[j0] < g[j1],则g[j0]g[j1]都有可能被dp[i]用到;而若g[j0] >= g[j1],则只有g[j1]可能被用到。也就是说,右侧的更小的值,能够把左侧更大的值给挡住,左侧更大的值不可能作为答案。

所以,我们可以只维护单调递增的那些g[j],那么该用单调队列还是单调栈呢?

答案是单调队列。由于队列里的g[j]是单调递增的,则我们计算dp[i]时,先取队头,因为队头是最小的g[j],而我们dp[i]的定义就是要取最小的行程数。看下这个j是否满足约束条件,若满足,则dp[i]应当由这个g[j]转移过来;若不满足约束条件,则对于更大的i,此j一定更不满足约束条件(观察上面的不等式即可),所以该g[j]不可能再被后续更大的i所用上。此时应当弹出队头。一直弹出队头,直到遇到一个满足约束条件的j,用此时的g[j]来计算出dp[i]。由于队列中每个j只可能入队和出队一次,一共要计算n个状态,平摊到每个状态上的计算量就是 O ( 1 ) O(1) O(1),总的时间复杂度是 O ( n ) O(n) O(n)

这个单调队列的优化,不是那么容易能看出来的,感觉需要大量的做题经验。(反正我是虽然能推出限制条件的不等式和状态转移方程,但你让我想破脑袋我也想不出要用单调队列。QAQ

// C++ 384ms  动态规划+单调队列
typedef long long LL;
class Solution {
public:
    int boxDelivering(vector<vector<int>>& boxes, int portsCount, int maxBoxes, int maxWeight) {
        int n = boxes.size();
        
        vector<LL> preWeight(n + 1); // 重量的前缀和
        for (int i = 1; i <= n; i++) preWeight[i] = preWeight[i - 1] + boxes[i - 1][1];
        // 这个前缀和好像没什么用?  不!有用!

        // 需要每个区间内的行程数, 包括区间2个端点, 加上区间内相邻不同数字的个数总和
        vector<int> preCnt(n + 1);
        preCnt[1] = 0; // n = 1
        for (int i = 1; i < n; i++) {
            preCnt[i + 1] = preCnt[i];
            if (boxes[i][0] != boxes[i - 1][0]) preCnt[i + 1]++;
        }

        // preCnt[i] - preCnt[j] 就是中间不同的点的个数

        // 本质就是将一个线段, 在某些约束下, 切割成若干个子线段
        // dp[i] 表示运输[0, i]这些区间内的箱子, 需要的最少行程次数
        vector<int> dp(n + 1);
        // 需要枚举前一个分割点j, j从1开始
        // dp[i] = dp[j - 1] + preCnt[i] - preCnt[j] + 2
        // 其中需要满足 
        // i - j + 1 <= maxBoxes
        // preWeight[i] - preWeight[j - 1] <= maxWeight
        // 状态转移变换
        // dp[i] = dp[j - 1] - preCnt[j] + preCnt[i] + 2
        // 令 g[j] = dp[j - 1] + preCnt[j]
        // 则 dp[i] = g[j] + preCnt[i] + 2
        // 需要满足的限制条件是
        // j >= i + 1 - maxBoxes
        // preWeight[j - 1] >= preWeight[i] - maxWeight
        // 单调队列, 队列中保持单调递增
        deque<int> q; // q中存j的值, 需要一个双端队列
        for (int i = 1; i <= n; i++) {
            // 队头不满足条件的先弹出
            while (!q.empty()) {
                int j = q.front();
                if (j < i + 1 - maxBoxes || preWeight[j - 1] < preWeight[i] - maxWeight) q.pop_front();
                else break;
            }
            // 队尾需要和当前的比较, 并保持递增
            while (!q.empty()) {
                int j = q.back();
                if (dp[j - 1] - preCnt[j] >= dp[i - 1] - preCnt[i]) q.pop_back();
                else break;
            }
            q.push_back(i);
            int j = q.front();
            dp[i] = dp[j - 1] - preCnt[j] + preCnt[i] + 2;
        }
        return dp[n];
    }
};

image-20221209112925820

总结

这道题目还是非常的有难度的,难度估分达到了2600分。我花了2天才把这道题搞明白,太不容易了!(TAT

此题考察的知识点也比较多,需要将多种经典算法组合起来,包含了动态规划,前缀和,单调队列或贪心。值得好好消化理解。

单调队列的优化思路尤其难想,需要对等式,不等式进行变换,以及观察规律。

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

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

相关文章

web前端期末大作业:基于HTML+CSS+JavaScript制作鲜花礼品在线购物网站设计(19页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

代码随想录刷题Day59 | 503. 下一个更大元素 II | 42. 接雨水

代码随想录刷题Day59 | 503. 下一个更大元素 II | 42. 接雨水 503. 下一个更大元素 II 题目&#xff1a; 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的…

云与开源,共植数字世界的根

摘要&#xff1a;本文整理自阿里巴巴集团副总裁、阿里巴巴开源技术委员会负责人贾扬清&#xff0c;在 Flink Forward Asia 2022 主会场的开场致辞。Tips&#xff1a;点击「阅读原文」获取演讲 ppt&#xff5e;云和开源&#xff0c;共生、共长、共植数字世界的根。从在云上使用开…

SAP PS 第17节 项目产成品产出

SAP PS 第17节 项目产成品产出及差异处理1 模拟场景说明1.1 拖拽负库存1.2 发料原材料及报工1.3 执行副产品入库migo发预留1.4 CNS0交货1.5 后面开票产生收入按照项目结算即可项目上有一类比较另类的玩法&#xff0c;就是舍弃PP&#xff0c;依靠网络活动的负库存&#xff0c;实…

Web前端大作业—个人网页(html+css+javascript)我的家乡新密 (15页)含课程设计

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

2030年销售额突破200亿美元!瑞萨电子揭秘智能汽车版图

汽车正在成为继手机之后的下一个智能终端&#xff0c;并且已经成为全球各大芯片头部厂商的必争之地。 过去&#xff0c;汽车芯片市场主要由恩智浦、瑞萨电子、TI等传统汽车芯片巨头垄断&#xff0c;外来者鲜有机会可以入局。但近几年&#xff0c;包括高通、英特尔等全球各大芯…

【无人机】基于Fast行军树(FMT)求解无人机故障路径规划问题附matlab代码和论文

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

ElementPlus弹窗之后让外部区域可编辑

原始的el-dialog弹出以后外部区域是不可编辑的。 为最外层的父元素添加样式 同时给dialog本身添加样式

【工作日推算】JS计算当前时间前N个工作日(去除周末及节假日,文尾附源码下载)

【写在前面】前些日子忙了几天有关指标对比分析的功能&#xff0c;因为系统是对接券商类的业务&#xff0c;所以他们比较关注的是工作日的数据波动&#xff0c;因此前端指标对比数据需要拿工作日的&#xff0c;不然他们停市的数据比较也没用&#xff0c;故而今天针对之前实现的…

如何快速搞懂一家公司?

如果没有快速作为前提&#xff0c;你的搞懂&#xff0c;价值会大打折扣。 一.研究一家公司需要的宏观视野 1.把握长期明确趋势 看清宏观大背景能为你搞懂公司做出铺垫&#xff0c;同时看清这个公司和宏观的密切程度是怎样的&#xff0c;也决定了需要多大程度关注宏观变化。 …

【基于Pycharm的Django3教程】Part1:初识Django

文章目录1 初识Django1.1 django的安装1.2 创建django项目1.3 两种创建方式的对比1.4 默认文件介绍1.5 APP的创建和说明1.6 启动运行django1.7 模板和静态文件1.8 模板语法1.9 请求和响应1.10 orm数据库操作1.11 ORM 数据库案例&#xff1a;用户管理1 初识Django 1.1 django的…

22 条 API 设计的最佳实践

在这个微服务的世界里&#xff0c;后端API的一致性设计是必不可少的。 今天&#xff0c;我们将讨论一些可遵循的最佳实践。我们将保持简短和甜蜜——所以系好安全带&#xff0c;出发咯&#xff01; 首先介绍一些术语 任何API设计都遵循一种叫做“面向资源设计”的原则&#…

TOWER x Binance NFT 桥接教程

TOWER x Binance NFT 销售的 TOWER 门票和人物化身皮肤 NFT 现在可以从 BNB 链桥接到 Polygon 啦&#xff01; 一起来看看如何将你的 TOWER x Binance NFT 从 BNB 链转移到 Polygon&#x1f447; 1、到 BinanceNFT 用户中心提取你的 TOWER NFT 到 BNB Chain&#xff0c;然后等待…

html多个好看的背景动态效果(附源码)

文章目录1.设计来源1.1 图片轮动背景1.2 星空流星背景1.3 动态美女背景1.4 动态屋雨背景1.5 动态街道背景1.6 动态夜幕背景2.效果和源码2.1 动态效果2.2 透明度配置2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/arti…

在线绘制富集分析多组气泡图和单细胞分析marker基因矩阵气泡图

常规的GO或者KEGG通路富集分析结果通常以气泡图的形式展示&#xff0c;然而这个气泡图仅仅是一个比较的结果&#xff0c;如果想在一张图上展示多个比较的结果&#xff0c;就需要用到多组气泡图&#xff08;图1&#xff0c;左侧&#xff09;。 单细胞RNA-seq分析结果中&#xf…

delphi异步与javascript

delphi及C Builder异步处理与javascript 目录 delphi及C Builder异步处理与javascript 1、用于实现异步事件、异步方法、及其异步结果回调的可自定义的通用类型 2、你可引用以下基于接口化对象和异步结果的接口的抽象类&#xff0c;去实现异步方法或异步事件的自定义类 2.…

关于DDoS攻击,这些基本概念你一定要知道!

什么是DDoS攻击 DDoS是Distributed Denial of Service的简称&#xff0c;中文是分布式拒绝服务。 这有点拗口吧&#xff1f; 这样&#xff0c;我们先理解下DDoS的前身DoS&#xff08;Denial of Service&#xff09;&#xff0c;即拒绝服务。 最基本的DoS攻击就是攻击者利用…

基础--吊打面试官--精通synchronized底层实现原理

synchonized是一个字段 1.0之前太慢&#xff0c;重&#xff0c;jdk1.0后修改&#xff0c;变得轻.修改的原理是&#xff1a;以前是涉及到用户态和内核态的交互&#xff0c;现在是用户态实现。 基本概念理解&#xff1a; 用户态和内核态的概念&#xff1a;程序的不同级别。内核态…

Vue学习:事件处理(与用户产生交互-点击)

Vue对元素绑定事件&#xff0c;需要使用指令&#xff0c;也就是v-开头 v-on&#xff1a;当什么什么时候时候 点击-出现弹窗&#xff1a;使用method方法 <!-- 准备容器 --><div idroot> <h2>欢迎页面&#xff0c;你好 {{name}}</h2><!-- v-on:click…

(附源码)小程序 法律全书 毕业设计 280844

小程序spring boot法律全书管理系统 摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;小程序法律全书被用…