算法与数据结构29:四边形不等式技巧

news2024/11/15 4:16:16

四边形不等式技巧

  • 题目一
  • 题目二
  • 题目三
  • 四边形不等式技巧
  • 题目四
  • 题目五

题目一

给定一个非负数组arr,长度为N,
那么有N-1种方案可以把arr切成左右两部分
每一种方案都有,min{左部分累加和,有部分累加和}
求这么多方案中,min{左部分累加和,有部分累加和}的最大值是多少?
整个过程要求时间复杂度O(N)

解法:
1、先求出数组累加和sum
2、每一个位置的切分,都有左部分累加和leftSum,和有部分rightSum
3、一开始leftSum=0,rightSum=sum,枚举每个切分位置,leftSum不断加入数,rightSum不断减去数
4、每次抓一个答案min(leftSum,rightSum)去PK一下

/**
 * 给定一个非负数组arr,长度为N,
 * 那么有N-1种方案可以把arr切成左右两部分
 * 每一种方案都有,min{左部分累加和,有部分累加和}
 * 求这么多方案中,min{左部分累加和,有部分累加和}的最大值是多少?
 * 整个过程要求时间复杂度O(N)
 * Created by huangjunyi on 2022/12/10.
 */
public class PostOfficeProblem_0 {

    public static int splitArray(int[] arr) {
        if (arr == null || arr.length < 2) return 0;
        // 求出整体累加和sum
        int sum = 0;
        for (int num : arr) sum += num;
        int max = 0;
        // 左部分累加和
        int leftSum = 0;
        // 枚举每个切分位置
        for (int i = 0; i < arr.length; i++) {
            // 左部分增加进入左侧的数
            leftSum += arr[i];
            // 抓一个min(left,right)样本,PK一下
            max = Math.max(max, Math.min(leftSum, sum - leftSum));
        }
        return max;
    }

}

题目二

把题目一种提到的,
min{左部分累加和,有部分累加和},定义为S(N-1),也就是说:
S(N-1):在arr[0…N-1]范围上,做最优划分所得到的min{左部分累加和,右部分累加和}的最大值
现在要求返回一个长度为N的s数组,
s[i]=在arr[0…i]范围上,做最优划分所得到的min{左部分累加和,右部分累加和}的最大值
得到整个s数组的过程,做到时间复杂度O(N)

一般想到的解法是,枚举每一个子数组结束位置,然后用题目一的方法
但是这种解法的时间复杂度是O(N^2)
这种解法存在重复计算的过程,而实际上枚举的切分位置是不需要回退的,假设我上一轮得出的最优切分位置是9,现在进入下一轮了,为什么切分位置不用回退,只要继续往右呢?
有三种情况:
1、上一轮左部分的累加和比右部分小,进入下一轮,如果切分位置回退了,那左部分不就更小了?所以不用回退
2、上一轮左部分的累加和比右部分大,进入下一轮,右部分却比左部分大了,左部分比右部分小,那也不用回退,因为回退了,左部分不也是更小吗?
3、上一轮左部分的累加和比右部分大,进入下一轮,还是左部分的累加和比右部分大,也不用回退。因为回退能得更大的答案的话,上一轮就不会再得出这个位置是最优切分位置。如果回退了,右部分还是更小,但是得到的答案更大,那么上一轮就应该在前一个位置;如果回退了,左部分更小,但是得到的答案更大,那么上一轮也应该在在前一个位置,因为上一轮的前一个切分位置的左部分累加和也是这个,而右部分的累加和反而增加了,那么不管minSum是左侧还是右侧,都比切分位置往后移一位要大,而这个结论是和当前这种情况的前提是相违背的。

/**
 * 把题目一种提到的,
 * min{左部分累加和,有部分累加和},定义为S(N-1),也就是说:
 * S(N-1):在arr[0...N-1]范围上,做最优划分所得到的min{左部分累加和,右部分累加和}的最大值
 * 现在要求返回一个长度为N的s数组,
 * s[i]=在arr[0...i]范围上,做最优划分所得到的min{左部分累加和,右部分累加和}的最大值
 * 得到整个s数组的过程,做到时间复杂度O(N)
 * Created by huangjunyi on 2022/12/10.
 */
public class PostOfficeProblem_1 {

    public static int[] splitArray(int[] arr) {
        if (arr == null || arr.length == 0) return new int[0];
        int N = arr.length;
        int[] res = new int[N];
        res[0] = 0;
        int leftSum = 0; // 左部分累加和
        int rightSum = arr[0]; // 右部分累加和
        int bestSplit = -1; // 最优划分位置,一开始是0~0范围,肯定是左边一个数也没有,右边有一个数,所以切在-1位置
        for (int range = 1; range < N; range++) {
            rightSum += arr[range]; // 范围扩大了
            while (bestSplit + 1 < range) {
                int before = Math.min(leftSum, rightSum); // 切分位置不动,min{左部分累加和,右部分累加和}
                int after = Math.min(leftSum + arr[bestSplit + 1], rightSum - arr[bestSplit + 1]); // 切分位置往后挪一位, min{左部分累加和,右部分累加和}
                if (after >= before) { // 切分位置往后挪一位,结果更好
                    leftSum = leftSum + arr[bestSplit + 1];
                    rightSum = rightSum - arr[bestSplit + 1];
                    bestSplit++; // 切分位置往后挪一位,结果更好,就往右挪了
                } else {
                    break;
                }
            }
            res[range] = Math.min(leftSum, rightSum);
        }
        return res;
    }

}

题目三

摆放着n堆石子。现要将石子有次序地合并成一堆
规定每次只能选相邻的2堆石子合并成新的一堆,
并将新的一堆石子数记为该次合并的得分
求出将n堆石子合并成一堆的最小得分(或最大得分)合并方案

这一题可以用范围尝试模型对应的dp解
dp[L][R]表示从L~R范围上,最优合并方案的最小代价
假设,arr = [1,4,2,3]
dp表的两条对角线可以填好
下半部分是没有用的,因为L>R
在这里插入图片描述
那么其他格子怎么求呢?
比如求dp[1][3],也就是1~3范围的最右合并方案的最小代价
其实看最后一次合并时的最优划分方案,就转换成了上一题的问题
1~1和2~3合并,1~2和3~3合并
所以求dp[1][3],就看dp[1][1]+dp[2][3]、dp[1][2]+dp[3][3],谁更小,再加一个1~3范围的累计和
dp[0][3]同理,0~0和1~3合并,0~1和2~3合并,0~2和3~3合并

    public static int mergeStone(int[] arr) {
        if (arr == null || arr.length < 2) return 0;
        int N = arr.length;
        // 初始化dp表的两条对角线,因为dp[i][i]对角线本来就是0,所以不用管
        int[][] dp = new int[N][N];
        for (int i = 0; i < N - 1; i++) {
            dp[i][i + 1] = arr[i] + arr[i + 1];
        }
        // 求一个前缀和数组,后面要用到
        int[] preSum = new int[N];
        preSum[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
            preSum[i] = preSum[i - 1] + arr[i];
        }
        /*
        dp[L][R]表示从L~R范围上,最优合并方案的最小代价
        枚举最后一次合并的切分方案
        比如dp[0][3],0~3范围最优合并方案的最小代价
        最后一次合并的切分方案就有:
        0~0和1~3合并,0~1和2~3合并,0~2和3~3合并
        所以dp[0][3] = min(dp[0][0]+dp[1][3], dp[0][1]+dp[2][3], dp[0][2]+dp[3][3]) + sum(arr, 0, 3)
         */
        for (int L = N - 3; L >= 0; L--) {
            for (int R = L + 2; R < N; R++) {
                dp[L][R] = Integer.MAX_VALUE;
                for (int split = L; split < R; split++) {
                    dp[L][R] = Math.min(dp[L][R], dp[L][split] + dp[split + 1][R]);
                }
                // + sum(arr, L, R)
                dp[L][R] = dp[L][R] + preSum[R] - (L == 0 ? 0 : preSum[L - 1]);
            }
        }
        // 从0~N-1范围上,最优合并方案的最小代价
        return dp[0][N - 1];
    }

但是这样子,在求每个格子时就要枚举行为
而且枚举的是切分位置,那是不是可以像上一题一样,保存上一轮的切分位置,这一轮就不用回退了?
所以再搞一个bestSplit[][]
bestSplit[L][R]表示的是在求dp[L][R]时的最优切分位置

    public static int mergeStone2(int[] arr) {
        if (arr == null || arr.length < 2) return 0;
        int N = arr.length;
        // 初始化dp表和bestSplit表的两条对角线,因为dp[i][i]对角线本来就是0,bestSplit[i][i]只有一个数,也没得切,所以不用管
        int[][] dp = new int[N][N];
        int[][] bestSplit = new int[N][N];
        for (int i = 0; i < N - 1; i++) {
            dp[i][i + 1] = arr[i] + arr[i + 1];
            bestSplit[i][i + 1] = i;
        }
        // 求一个前缀和数组,后面要用到
        int[] preSum = new int[N];
        preSum[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
            preSum[i] = preSum[i - 1] + arr[i];
        }
        /*
        dp[L][R]表示从L~R范围上,最优合并方案的最小代价
        枚举最后一次合并的切分方案
        比如dp[0][3],0~3范围最优合并方案的最小代价
        最后一次合并的切分方案就有:
        0~0和1~3合并,0~1和2~3合并,0~2和3~3合并
        所以dp[0][3] = min(dp[0][0]+dp[1][3], dp[0][1]+dp[2][3], dp[0][2]+dp[3][3]) + sum(arr, 0, 3)

        bestSplit[L][R]表示的是在求dp[L][R]时的最优切分位置
        用于优化枚举行为,切分位置不用回退
         */
        for (int L = N - 3; L >= 0; L--) {
            for (int R = L + 2; R < N; R++) {
                dp[L][R] = Integer.MAX_VALUE;
                // 切分位置在上一轮的基础上往后尝试
                int best = bestSplit[L][R - 1];
                // 当前L~R的最优切分位置,肯定在getBestSplit(L, R-1)和getBestSplit(L+1, R)之间
                // 所以就在bestSplit[L][R - 1]~bestSplit[L + 1][R]之间
                for (int split = bestSplit[L][R - 1]; split <= bestSplit[L + 1][R]; split++) {
                    if (dp[L][split] + dp[split + 1][R] < dp[L][R]) {
                        dp[L][R] = Math.min(dp[L][R], dp[L][split] + dp[split + 1][R]);
                        best = split; // 更新切分位置
                    }
                }
                // + sum(arr, L, R)
                dp[L][R] = dp[L][R] + preSum[R] - (L == 0 ? 0 : preSum[L - 1]);
                // 记录最优切分位置
                bestSplit[L][R] = best;
            }
        }
        // 从0~N-1范围上,最优合并方案的最小代价
        return dp[0][N - 1];
    }

这种优化区间范围划分的技巧,就是四边形不等式技巧

四边形不等式技巧

1、两个可变参数的区间划分问题
2、每个格子有枚举行为
3、当两个可变参数固定一个,另一个参数和答案之间存在单调关系
4、而且往往是反向单调关系
5、枚举加速的位置对:上+右,或者,左+下
6、不要证明!用对数器验证!
7、可以把时间复杂度降低一阶

题目四

力扣第410题
https://leetcode.cn/problems/split-array-largest-sum/
给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组。
设计一个算法使得这 m 个子数组各自和的最大值最小。

示例 :
输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。
其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

其实这个题目也可以转化为一个二维区间划分问题,怎么转化为区间二维划分问题呢?
假设现在要求L~R范围内,划分为m个子数组,各划分方案下各子数组累加和中最大值中的最小值
就看前m-1个子数组和最后一个子数组,怎么切分,枚举切分位置
那么m-1个子数组的答案怎么求呢?前m-2个子数组和第m-1个子数组,怎么切分,枚举切分位置

所以搞一个二维dp表
dp[i][j]表示从数组0~i位置,分成j个子数组,各子数组累加和中最大值中的最小值
dp[i][j]中的leftMax就枚举dp[i][j-1]、dp[i-1][j-1]、dp[i-2][j-1]、…,rightSum则通过前缀和数组求,dp[i][j] = min(dp[i][j], min(leftMax, rightSum))

    class Solution {
        public int splitArray(int[] nums, int k) {
            int N = nums.length;
            int[][] dp = new int[N][k + 1];
            // 初始化dp表第0行,0~0范围,分i个子数组,最大的累加和
            for (int i = 1; i <= k; i++) {
                dp[0][i] = nums[0];
            }
            // 初始化dp表第0列,0~i范围,分0个子数组,最大的累加和,无效,不用管
            // 初始化dp表第1列,0~i范围,分1个子数组,最大的累加和,其实就是nums数组前缀和,可以同时把前缀和数组生成好,反正后面要用
            int[] preSum = new int[N];
            preSum[0] = nums[0];
            dp[0][1] = preSum[0];
            for (int i = 1; i < N; i++) {
                preSum[i] = preSum[i - 1] + nums[i];
                dp[i][1] = preSum[i];
            }
            /*
            dp[i][j]:
            从0~i,分j个子数组,子数组各自累加和中的最大值,中的最小值(最优划分方案下)
            0~i,切2部分,左部分前j-1个子数组分,后一部分给最后一个数组
            而左部分前j-1个子数组划分的答案,之前已经求过
            dp[i][j] = min(max(dp[0][j-1], rightSum), max(dp[1][j-1], rightSum), max(dp[2][j-1], rightSum), ......)
             */
            for (int i = 1; i < N; i++) {
                for (int j = 2; j <= k; j++) {
                    dp[i][j] = Integer.MAX_VALUE;
                    for (int split = 0; split < i; split++) {
                        dp[i][j] = Math.min(dp[i][j], Math.max(dp[split][j - 1], preSum[i] - preSum[split]));
                    }
                }
            }
            // 在0~N-1范围上分k个子数组,子数组各自累加和中的最大值,在最右划分方案下的最小值
            return dp[N - 1][k];
        }
    }

然后可以在上面的解法基础上,使用四边形不等式优化
另外准备一个bestSplit[][]表,bestSplit[i][j]存求dp[i][j]是的最优切分位置
但是要修改一些填dp表的顺序,整体上从左往右填,每一列从下往上填

    class Solution {
        public int splitArray(int[] nums, int k) {
            int N = nums.length;
            int[][] dp = new int[N][k + 1];
            // split[i][j]存求dp[i][j]是的最优切分位置
            int[][] bestSplit = new int[N][k + 1];
            for (int i = 1; i <= k; i++) {
                dp[0][i] = nums[0];
                // 0~0范围,分i分,只有1个数可以分
                bestSplit[0][i] = 0;
            }
            int[] preSum = new int[N];
            preSum[0] = nums[0];
            dp[0][1] = preSum[0];
            // 0~0范围,分1分,只有1个数可以分
            bestSplit[0][1] = 0;
            for (int i = 1; i < N; i++) {
                preSum[i] = preSum[i - 1] + nums[i];
                dp[i][1] = preSum[i];
                // 0~i范围,分1分,那就是不用分,切分为在-1
                bestSplit[i][1] = -1;
            }
            // 整体一列一列从左往右填
            for (int j = 2; j <= k; j++) {
                // 单独一列从下往上填,用四边形不等式优化
                for (int i = N - 1; i >= 1; i--) {
                    dp[i][j] = Integer.MAX_VALUE;
                    // 最优切分位置,只在bestSplit[i][j - 1]和bestSplit[i +1][j]中间范围枚举
                    int best = bestSplit[i][j - 1] == -1 ? 0 :  bestSplit[i][j - 1];
                    for (int split = best; split <= (i < N - 1 ? bestSplit[i + 1][j] : N - 1); split++) {
                        int leftMin = split == -1 ? 0 : dp[split][j - 1];
                        int rightSum = split == i ? 0 : preSum[i] - preSum[split];
                        if (Math.max(leftMin, rightSum) < dp[i][j]) { // 这里 <? 还是 <=?,是一个边界条件,不同题目是不一样的,要都试一下
                            dp[i][j] = Math.max(leftMin, rightSum); // 切分位置往右挪,结果更好
                            best = split; // 更新最优切分位置
                        }
                    }
                    bestSplit[i][j] = best; // 保存最优切分位置
                }
            }
            return dp[N - 1][k];
        }
    }

题目五

一条直线上有居民点,邮局只能建在居民点上。给定一个有序正数数组arr,每个值表示居民点的一维坐标,再给定一个正数num,表示邮局数量。选择num个居民点建立num个邮局,使所有的居民点到最近邮局的总距离最短,返回最短的总距离。
【举例】
arr = [1,2,3,4,5,1000],num = 2
第一个邮局建立在3位置,第二个邮局建立在1000位置。那么1位置到邮局的距离为2,2位置到邮局距离为1,3位置到邮局距离为0,4位置到邮局的距离为1,5位置到邮局的距离为2,1000位置到邮局的距离为0。这种方案下的总距离为6,其他任何方案的总距离都不会比该方案的总距离更短,所有返回6

没有使用四边形不等式优化时的版本:

    public static int minDis(int[] arr, int num) {
        /*
        生成一个N行N列二维数组record,recode[i][j]表示i~j范围上建一个邮局,最短距离是多少
        生成一张N行num列二维表dp,dp[i][j]表示0~i号居民点,建立j个邮局,最短距离是多少
        dp表的生成依赖到recode表
         */
        if (arr == null || arr.length < 2 || num < 1) return 0;
        int[][] recode = getRecode(arr);
        int N = arr.length;
        int[][] dp = new int[N][num + 1];

        /*
        dp表第0列不用填,0个邮局,距离无限大
        dp表第0行不用填,1个居民点,距离都是0
        先初始化第1列
         */
        for (int i = 0; i < N; i++) {
            dp[i][1] = recode[0][i];
        }

        /*
         填剩下的
         j大于等于i的也不用填
         因为那表示邮局数量最少和居民点一样多
         那就一人一个,距离为0
          */
        for (int i = 1; i < N; i++) {
            for (int j = 2; j <= Math.min(i, num); j++) {
                // 初始化dp[i][j],表示0~i居民点都由最后一个邮局负责
                dp[i][j] = recode[0][i];
                // 枚举最后一个居民点的负责范围 k ~ i <= 这里存在枚举行为,可以用四边形不等式优化
                for (int k = i; k > 0; k--) {
                    dp[i][j] = Math.min(dp[i][j], dp[k - 1][j - 1] + recode[k][i]);
                }
                // 还有一个是0~i范围,都由前j-1个邮局负责,最后一个邮局歇菜,不过也不可能
                dp[i][j] = Math.min(dp[i][j], dp[i][j - 1]);
            }
        }
        return dp[N - 1][num];
    }

    private static int[][] getRecode(int[] arr) {
        /*
        每次都假设邮局正处于范围的中间
        就recode[L][R]时,就是L~R-1范围的距离recode[L][R - 1],加上新增居民点离邮局的距离
        只需要填上半部分,下半部分不需要填,因为L不可能大于R
         */
        int N = arr.length;
        int[][] recode = new int[N][N];
        for (int L = 0; L < N; L++) {
            for (int R = L + 1; R < N; R++) {
                recode[L][R] = recode[L][R - 1] + arr[R] - arr[(L + R) >> 1];
            }
        }
        return recode;
    }

使用四边形不等式技巧优化后的版本:

/**
 * 给定一个数组arr,arr为正整数有序数组,代表居民点坐标
 * 再给定一个正整数num,代表邮局数量
 * 邮局可以建在任意居民点上
 * 求邮局离居民点的最短距离
 *
 * 用四边形不等式优化枚举行为
 * 只要符合三种情况:
 * 1)存在枚举行为
 * 2)每个格子和它上、右或下、左存在某种单调关系
 * 3)是一个区间划分问题
 * 就可以用四边形不等式尝试优化,优化的时枚举行为,缩短枚举行为的范围
 * 用一个choose[i][j]记录dp[i][j]是枚举行为最优取值的枚举值k
 * 下次填其他格子时就可以通过choose记录的当时获得最优值的枚举值,作为其枚举行为的上界或下界,缩短枚举行为的范围
 *
 * 还有第四个条件
 * 4)dp[i][j]不会同时依赖本行和本列的某个值或某些值,即最多只会依赖本行某个值或本列某个值,或者都不依赖
 * 比如只依赖本列的值,那就从上往下填,然后在从右往左,一个格子就依赖上一个格子和右边格子得出枚举k的上下界
 * 比如只依赖本行的值,那就从左往右填,然后从下往上填,一个格子就依赖左边格子和下以边格子得出枚举k的上下界
 * 比如只依赖左边和左上方的左右格子,但不依赖本列,可以先填好第一个列,然后从第二列最底下一个格子从下往上,从左往右填,一个格子就依赖左边和下边格子得出枚举k的上下界
 *
 * 优化的原理:
 * 比如:在数组上切一刀,求min(max(leftSum, rightSum))
 * 如果数组在0~N范围上切一刀,切在i位置
 * 现在数组长度加1,求0~N+1,那么其实不需要从0开始尝试,从i开始即可
 * 因为0~N和0~N+1是存在某种单调关系的
 * 0~N在i位置且一刀是最优解
 * 如果0~N+1在i左边的位置切一刀,肯定不是最优解,因为rightSum会更大
 *
 * Created by huangjunyi on 2022/10/15.
 */
public class _02PostOfficeProblem {

    public static int minDis(int[] arr, int num) {
        /*
        生成一个N行N列二维数组record,recode[i][j]表示i~j范围上建一个邮局,最短距离是多少
        生成一张N行num列二维表dp,dp[i][j]表示0~i号居民点,建立j个邮局,最短距离是多少
        dp表的生成依赖到recode表
         */
        if (arr == null || arr.length < 2 || num < 1) return 0;
        int[][] recode = getRecode(arr);
        int N = arr.length;
        int[][] dp = new int[N][num + 1];

        /*
        dp表第0列不用填,0个邮局,距离无限大
        dp表第0行不用填,1个居民点,距离都是0
        先初始化第1列
         */
        for (int i = 0; i < N; i++) {
            dp[i][1] = recode[0][i];
        }

        /*
        有一个N行N列的二维表,记录填dp[i][j]是,k当时的值是多少
        那么在填一个格子的时候,枚举k的范围,就有一个下界和上界
        k的下界就是它上一个格子的k的取值,下界就是它右边一个格子的取值
         */
        int[][] choose = new int[N][N];

        /*
         填剩下的
         j大于等于i的也不用填
         因为那表示邮局数量最少和居民点一样多
         那就一人一个,距离为0
          */
        for (int i = 1; i < N; i++) {
            for (int j = Math.min(i, num); j >= 2; j--) {

                int down = choose[i - 1][j]; // 枚举k时的下界
                int up = j == Math.min(i, num) ? i : choose[i][j + 1]; // 枚举k时的上界

                // 初始化dp[i][j],表示0~i居民点由一个邮局负责
                dp[i][j] = recode[0][i];
                // 枚举最后一个居民点的负责范围 k ~ i <= 这里用四边形不等式优化,枚举范围从1~i缩短到从down~up
                for (int k = Math.max(1, down); k <= Math.min(i, up); k++) {
                    if (dp[k - 1][j - 1] + recode[k][i] < dp[i][j]) {
                        dp[i][j] = dp[k - 1][j - 1] + recode[k][i];
                        choose[i][j] = k;
                    }
                }
            }
        }
        return dp[N - 1][num];
    }

    private static int[][] getRecode(int[] arr) {
        /*
        每次都假设邮局正处于范围的中间
        就recode[L][R]时,就是L~R-1范围的距离recode[L][R - 1],加上新增居民点离邮局的距离
        只需要填上半部分,下半部分不需要填,因为L不可能大于R
         */
        int N = arr.length;
        int[][] recode = new int[N][N];
        for (int L = 0; L < N; L++) {
            for (int R = L + 1; R < N; R++) {
                recode[L][R] = recode[L][R - 1] + arr[R] - arr[(L + R) >> 1];
            }
        }
        return recode;
    }

}

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

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

相关文章

我当PM,把ChatGPT玩冒(bà)烟(gōng)了

最近ChatGPT太火&#xff0c;本拐也注册了一个。用着确实是十分上头。而且事实证明&#xff0c;在处理明确的工程类业务时&#xff0c;ChatGPT可以让程序员事倍功半&#xff08;也有可能饭碗不保&#x1f648;&#xff09;参见&#xff1a; 跟着ChatGPT手把实现一个websocket连…

数据管理 Valentina Studio Pro 12.6 Crack

Valentina Studio 被描述为与 MySQL、SQL Server、Maria DB、SQLite、PostgreSQL 以及最后但并非最不重要的 Valentina DB 数据库一起用于工作流程的数据库工具的通用管理。Valentina Studio 能够允许用户和开发人员连接所有重要的数据库并运行查询&#xff0c;以及创建图表以了…

Hadoop如何保证自己的江湖地位?Yarn功不可没

前言 任何计算任务的运行都离不开计算资源&#xff0c;比如CPU、内存等&#xff0c;那么如何对于计算资源的管理调度就成为了一个重点。大数据领域中的Hadoop之所以一家独大&#xff0c;深受市场的欢迎&#xff0c;和他们设计了一个通用的资源管理调度平台Yarn密不可分&#x…

高性能缓存Caffeine的基本使用方式

文章目录介绍性能比对使用方式一、Population&#xff08;缓存类型&#xff09;1.Cache2.Loading3.Asynchronous (Manual)4.Asynchronously Loading二、Eviction&#xff08;驱除策略&#xff09;1.Size-based&#xff08;基于容量&#xff09;2.Time-based&#xff08;基于时间…

Redis Cluster 单机部署

高可用架构-Redis Cluster Redis服务器支持单机、主从复制、Sentinel、集群部署&#xff0c;部署架构也是由简单到复杂&#xff0c;Redis Cluster 集群架构是官方推荐应对大数据量、大并发场景部署方案。Redis的架构变迁如下图所示&#xff0c;其归属就是Redis Cluster Redis …

一款功能强大的课程报名系统 v6.2.0

内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 课程报名系统可为专为教育培训机构设计的在线选课报名系统&#xff0c;学员可综合考虑课程分类、适合人群、上课地点、上课时间、课程价格等多种选报最合适的班级 课程报名系统发布v6.2…

组件库技术选型和开发环境搭建

点击上方卡片“前端司南”关注我您的关注意义重大原创前端司南本文为稀土掘金技术社区首发签约文章&#xff0c;14天内禁止转载&#xff0c;14天后未获授权禁止转载&#xff0c;侵权必究&#xff01;专栏上篇文章传送门&#xff1a;基于Vite打造业务组件库&#xff08;开篇介绍…

盘点和总结秒杀服务的功能设计及注意事项技术体系

秒杀应该考虑哪些问题 超卖问题 分析秒杀的业务场景&#xff0c;最重要的有一点就是超卖问题&#xff0c;假如备货只有100个&#xff0c;但是最终超卖了200&#xff0c;一般来讲秒杀系统的价格都比较低&#xff0c;如果超卖将严重影响公司的财产利益&#xff0c;因此首当其冲…

0代码20min |微信接入chat-GPT

0代码20min | 微信接入chat-GPT 拥有一个openai账号 这个账号比较麻烦&#xff0c;需要国外手机号认证&#xff0c;这个的话可以直接参考这篇文章ChatGPT注册攻略 - 知乎 (zhihu.com)&#xff0c;这篇文章是用在线接受短信的虚拟号码 - SMS-Activate这个网站来注册账号的。 用…

《操作系统》期末考试试卷2参考答案

《操作系统》期末考试卷&#xff08;第2套&#xff09; 参考答案与评分标准 一、单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共30分&#xff09; 题 号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 答 案 D A C C B A D D A B D C A B D 二、填空题&#xff08;…

ADI Blackfin DSP处理器-BF533的开发详解31:汉字/ASIC II 字库(含源代码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 功能介绍 代码实现了汉字库和 ASIC II 码 字库的功能&#xff0c;代码会在编译时&#xff0c;将字库文件加载到内存中&#xff0c;所以必须使用 …

软件测试基础篇(6)

两个测试模型: 一:软件测试V模型:从上到下是一个开发模型&#xff0c;从下到上是一个测试模型(V模型也是瀑布模型的一个变种) 需求分析--->计划---->设计---->编码----->测试 用户需求 验收测试需求分析与…

java基于springboot的共享单车系统-计算机毕业设计

项目介绍 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Ma…

合并有序数组

目录 I.合并有序数组 Time Limit:1s Memory Limit: 256M Description: Input: Output: Sample input: Sample output: 约束&#xff1a; 思路代码: 1.暴力法 1.2暴力代码: 2.双指针(归并排序思路) 2.2双指针代码 总结: 题目链接: I.合并有序数组 Time Limit:1s Me…

专注推荐系统13年,我收获最大的4个成长

‍‍ 作者 | gongyouliu编辑 | gongyouliu我从2010年开始接触推荐系统&#xff0c;到现在差不多有13年了&#xff0c;这13年间我基本没有间断过对推荐系统的学习和实践&#xff08;今年还看了两本推荐系统相关的书籍和一些论文&#xff09;。回顾一下这十几年的经历&#x…

Java项目:SSM高校教职工差旅报销管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 管理员角色包含以下功能&#xff1a; 管理员登录,修改管理员资料,用户管理,公告管理,报销类型管理,系别信息管理,报销审核管理等功能。 用户角色…

Python webdriver.Chrome()的使用

1.前提 Python与Chrome路径下均安装chromedriver.exe。 2.chromedriver.exe版本选择及下载 下载地址为&#xff1a;http://npm.taobao.org/mirrors/chromedriver/ Chrome版本查看&#xff1a;浏览器右上角三个点->帮助->关于Google Chrome 找到对应的版本&#xff0c;主…

大数据面试之YARN常见题目

大数据面试之YARN常见题目 1 YARN工作机制 1.1 图解 上面有单词少个r&#xff0c;就不改了&#xff0c;大家知道就行。 1.2 文字描述 文字版描述&#xff1a; 1、MapReduce程序提交到Client所在节点&#xff0c;在MR程序的主函数当中有job.waitForCompletion()将任务进行提…

Linux网络-五种IO模型

Linux网络-高级IO零、前言一、什么是IO二、五种IO模型1、阻塞IO2、非阻塞IO3、信号驱动IO4、IO多路转接5、异步IO三、高级IO重要概念1、同步通信 vs 异步通信2、阻塞 vs 非阻塞3、其他高级IO零、前言 本章主要就Linux网络讲解非常重要的一个话题-高级IO 一、什么是IO IO是输入i…

20221211英语学习

今日新词&#xff1a; helplessly adv.无助地&#xff1b;无能为力地 physicist n.物理学家, 物理学研究者 capable adj.有能力的&#xff0c;有才能的&#xff1b;能胜任的&#xff1b;可以…的&#xff1b;容许…的 spokeswoman n.女发言人 production n.产量&#xff1…