动态规划设计

news2024/11/27 12:44:01

文章目录

  • 动态规划设计
  • 一、什么是最长递增子序列?
    • [300. 最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence/)
    • 注意子序列和子串的区别:
  • 二、动态规划解法
    • 2.1什么是数学归纳法?
    • 2.2最长递增子序列中数学归纳的实际应用
      • 如何找到动态规划的状态转移关系?
    • 2.3 在leetCode上通过的代码
  • 三、二分查找解法
    • 怎么用到二分查找解法?
  • 四、最长递增子序列的变种(拓展到二维)
    • [354. 俄罗斯套娃信封问题](https://leetcode.cn/problems/russian-doll-envelopes/)
      • 如何解决在二维数组上找最长递增子序列呢?
      • 补充知识:Arrary.sort用 Comparator比较器定制排序方式
      • 在leetCode上通过的代码:

动态规划设计

通过动态规划解题套路,我们知道了动态规划的套路:

  • 找到了问题的状态
  • 明确dp数组/函数的含义
  • 定义了base case

但是在掌握了这些之后,我们仍然不知道如何确定选择也就是找不到状态转移的关系,依然写不出动态规划解法(动态规划的难点就在于寻找正确的状态转移方程

针对这个问题,我们可以借助经典的最长递增子序列问题来学习设计动态规划的通用技巧:数学归纳思想

一、什么是最长递增子序列?

300. 最长递增子序列

最长递增子序列(Longest Increasing Subsequence,简写 LIS)是非常经典的一个算法问题

比较容易想到的是动态规划解法,时间复杂度为O(N^2)

比较难想到的是利用二分查找,时间复杂度是 O(NlogN)

比如说输入 nums=[10,9,2,5,3,7,101,18],其中最长的递增子序列是 [2,3,7,101],所以算法的输出应该是 4。

注意子序列和子串的区别:

子串一定是连续的,而子序列不一定是连续的

二、动态规划解法

动态规划的核心设计思想是数学归纳法

2.1什么是数学归纳法?

比如我们想证明一个数学结论,纳闷我们就会先假设这个结论在k<n时成立,然后根据这个假设,想办法推导证明出k=n的时候此结论也成立。

如果能够证明出来 那么就说明这个结论对于k等于任何数都成立

类似的,我们设计动态规划算法,需要一个dp数组。

我们可以假设dp[0...i-1] 都已经被算出来了,然后问自己:怎么通过这些结果算出 dp[i]

2.2最长递增子序列中数学归纳的实际应用

在此之前,我们首先要定义清楚dp数组的含义,即dp[i]的值到底代表什么?

dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度

在这里插入图片描述

为什么这样定义?

这就是解决子序列问题的一个套路

其实在动态规划问题中,dp数组的定义方法也就几种

算法演进的过程:

在这里插入图片描述

  1. 根据这个定义,我们就可以推出base case

    dp[i]初始值为1,因为以nums[i]结尾的最长递增子序列起码要包含它们自己

  2. 根据这个定义,我们的而最终结果(子序列的最大长度)应该是dp数组中的最大值

    int res = 0;
    for (int i = 0; i < dp.length; i++) {
        res = Math.max(res, dp[i]);
    }
    return res;
    
  3. 那现在我们要如何设计算法逻辑进行状态转移才能正确运行呢?也就是说我们如何使用数学归纳的思想

    假设我们已经知道了dp[0…4]的所有结果,我们如何通过这些已知结果推出dp[5]呢?

    在这里插入图片描述

    根据刚才我们对dp数组的定义,现在想求dp[5]的值,也就是想求以nums[5]为结尾的最长递增子序列

    nums[5]=3既然是递增子序列,

    我们只要找到前面那些结尾比3小的子序列

    然后把3接到这些子序列末尾就可以形成一个新的递增子序列而且这个新的子序列长度加1

    1. 那现在nums[5]前面有哪些元素小于nums[5]?

      我们可以用for循环比较一波就能把这些元素找出来

    2. 以这些元素为结尾的最长递增子序列的长度是多少?

      回顾我们对dp数组的定义,它记录的正是以每个元素为末尾的最长递增子序列的长度。

      以我们举得例子来说,nums[0]nums[4] 都是小于 nums[5] 的,然后对比 dp[0]dp[4] 的值,我们让 nums[5] 和更长的递增子序列结合,得出 dp[5] = 3

      在这里插入图片描述

      代码实现如下:

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

      i = 5 时,这段代码的逻辑就可以算出 dp[5]。其实到这里,这道算法题我们就基本做完了。

    3. 但是我们刚才只是算了 dp[5]dp[4], dp[3] 这些怎么算呢?

      类似数学归纳法,你已经可以算出 dp[5] 了,其他的就都可以算出来:

      for (int i = 0; i < nums.length; i++) {
          for (int j = 0; j < i; j++) {
              // 寻找 nums[0..j-1] 中比 nums[i] 小的元素
              if (nums[i] > nums[j]) {
                  // 把 nums[i] 接在后面,即可形成长度为 dp[j] + 1,
                  // 且以 nums[i] 为结尾的递增子序列
                  dp[i] = Math.max(dp[i], dp[j] + 1);
              }
          }
      }
      
  4. 整合代码如下

    int lengthOfLIS(int[] nums) {
        // 定义:dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度
        int[] dp = new int[nums.length];
        // base case:dp 数组全都初始化为 1
        Arrays.fill(dp, 1);
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) 
                    dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        
        int res = 0;
        for (int i = 0; i < dp.length; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
    

    时间复杂度为O(N^2)

    如何找到动态规划的状态转移关系?

    1. 明确dp数组的定义。这一步对于任何动态规划问题都很重要,如果不得当或者不够清晰,会阻碍之后的步骤

    2. 根据dp数组的定义,运用数学归纳法的思想,假设dp[0…i-1]都已知,求出dp[i],这部完成那么整个题目基本上就解决了

      如果无法完成这一步,很可能就是dp数组的定义不够恰当,需要重新定义dp数组的含义。也可能是dp数组存储的信息还不够,不足以推出下一步的答案,需要把dp数组扩大成二维数组甚至三维数组

2.3 在leetCode上通过的代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        int length=nums.length;
        int[] dp=new int[length];
        //base case
        Arrays.fill(dp,1);
        for(int i=0;i<length;i++){
            for(int j=0;j<i;j++){
                //在保证递增的情况下,寻找最长的长度
                if(nums[j]<nums[i]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
        }
        //寻找dp中的最大值
        int max=0;
        for(int k=0;k<length;k++){
            max=Math.max(max,dp[k]);
        }
        return max;
    }
}

三、二分查找解法

上面的解法是标准的动态规划,但对最长递增子序列问题来说,这个解法不是最优的,可能无法通过所有测试用例。

而二分查找是更高效的解法

怎么用到二分查找解法?

这个解法的时间复杂度为O(NlogN)

但是这个解法很难想到,所以掌握动态规划解法就行了

最长递增子序列和一种叫做patience game 的纸牌游戏有关,甚至有一种排序方法就叫做 patience sorting(耐心排序)。

现在通过一个简化的例子来理解算法的思路

现在有一排扑克牌,我们像遍历数组那样从左到右一张张地处理这些扑克牌

处理这些扑克牌要遵循以下规则:

只能把点数小的牌压到点数比它大的牌上

  • 如果当前牌点数较大,没有可以放置的堆,则新建一个堆,把这张牌放进去

  • 如果当前牌有多个堆可供选择,则选择最左边的那一堆放置

    但是为什么遇到多个可选择的堆时要放到最左边的堆上?

    因为这样可以保证牌堆顶的牌有序(2, 4, 7, 8, Q)

    在这里插入图片描述

如下图,这些扑克牌会被分成这样的5堆

纸牌A的牌面是最大的,纸牌2的牌面是最小的

在这里插入图片描述

按照上述规则执行,可以算出最长递增子序列,牌的堆数就是最长递增子序列的长度

在这里插入图片描述

每处理一张扑克牌就是要找到一个合适的牌堆顶来放,牌堆顶的牌有序,这就会用到二分查找,用二分查找来搜索当前牌应放置的位置

完整的代码如下:

int lengthOfLIS(int[] nums) {
    int[] top = new int[nums.length];
    // 牌堆数初始化为 0
    int piles = 0;
    for (int i = 0; i < nums.length; i++) {
        // 要处理的扑克牌
        int poker = nums[i];

        /***** 搜索左侧边界的二分查找 *****/
        int left = 0, right = piles;
        while (left < right) {
            int mid = (left + right) / 2;
            if (top[mid] > poker) {
                right = mid;
            } else if (top[mid] < poker) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        /*********************************/
        
        // 没找到合适的牌堆,新建一堆
        if (left == piles) piles++;
        // 把这张牌放到牌堆顶
        top[left] = poker;
    }
    // 牌堆数就是 LIS 长度
    return piles;
}

四、最长递增子序列的变种(拓展到二维)

354. 俄罗斯套娃信封问题

这道题目其实是最长递增子序列的一个变种,因为每次合法的嵌套是大的套小的,相当于在二维平面中找到一个最长递增的子序列,其长度就是最多能嵌套的信封个数

如何解决在二维数组上找最长递增子序列呢?

前面说的标准LIS算法只能在一维数组中寻找最长子序列,而我们的信封是由(w,h)这样的二维数组对形式表示的,如何把LIS算法运用过来呢?

试着想,我们可以通过w*h计算面积,然后对面积进行标准的LIS算法吗?

但这样的解法是不对的,比如1 * 10大于3 * 3,但是这样的两个信封是无法互相嵌套的

正确的解法是:

先对宽度 w 进行升序排序,如果遇到 w 相同的情况,则按照高度 h 降序排序;之后把所有的 h 作为一个数组,在这个数组上计算 LIS 的长度就是答案。

如图所示:

在这里插入图片描述

在这里插入图片描述

为什么这样就可以找到互相嵌套的信封序列呢?

  • 首先对宽度w从小到大排序,确保了w这个维度可以互相嵌套,所以我们只需要专注高度h这个维度能够互相嵌套
  • 然后两个w相同的信封不能相互包含,所以对于宽度w相同的信封,对高度h进行降序排序,保证二维LIS中不存在多个w相同的信封(因为题目说了长宽度相同也无法嵌套)

完整代码如下:

// envelopes = [[w, h], [w, h]...]
public int maxEnvelopes(int[][] envelopes) {
    int n = envelopes.length;
    // 按宽度升序排列,如果宽度一样,则按高度降序排列
    Arrays.sort(envelopes, new Comparator<int[]>() 
    {
        public int compare(int[] a, int[] b) {
            return a[0] == b[0] ? 
                b[1] - a[1] : a[0] - b[0];
        }
    });
    // 对高度数组寻找 LIS
    int[] height = new int[n];
    for (int i = 0; i < n; i++)
        height[i] = envelopes[i][1];

    return lengthOfLIS(height);
}

int lengthOfLIS(int[] nums) {
    // 见前文
}

当然以上两个方法也可以合并,这样就省下了hehight数组的空间

补充知识:Arrary.sort用 Comparator比较器定制排序方式

Arrary.sort()是用于给数组排序的,默认情况下是顺序排序,即从小到大

例如:实现Arrary.sort用 Comparator比较器定制排序方式

在实现之前要注意一点:

Comparator的定义,是用了泛型的。

public interface Comparator<T>{
    
}

泛型本质是引用,是不能传入基本数据类型(如int、long等)的,即T不能为 int 等,即Comparator会报错,要用Integer 代替 int。

而Comparator接口中包含了一个最核心的方法:int compare() ,这个方法是用于定义排序规则的,如下:

int compare(T o1,T o2);

compare的参数o1、o2 也是泛型T,即排序的元素也不能是基本数据类型。

由于 compare()比较抽象,所以我们可以这样记:

int compare(T o1, T o2) 是比较o1和o2的大小

  • 如果compare返回值为负数意味着o1比o2小

  • 否则返回为零意味着o1等于o2

  • 返回为正数意味着o1大于o2

    • 实现从小到大排序

      public class Test {
          public static void main(String[] args) {
              Integer[] arr = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
              Arrays.sort(arr, new Comparator<Integer>() {
                  @Override
                  public int compare(Integer o1, Integer o2) {
                      return o1-o2;
                  }
              });
          }
      }
      
    • 当我们需要逆序(大到小排列)的时候,只需要 return o2-o1

      即表示数值越大的数compare会认为它越小,数值越小的数compare反而会认为它越大

      • 当然我们实现逆序也可以将按从小到大顺序排序的数组反转即可

        public class Test {
         
            public static void main(String[] args) {
                Integer[] arr = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
                Arrays.sort(arr, Collections.reverseOrder());
            }
        }
        

        注意:如果用reverseOrder()的话,数组也是要设置成 Integer的。也是不能用基本数据类型。

      • 用lambda表达式更简介地实现Comparator

        public class Test {
            public static void main(String[] args) {
                Integer[] arr = {9, 8, 7, 2, 3, 4, 1, 0, 6, 5};
                Arrays.sort(arr, (o1,o2)->{return o2-o1;});
                }
            }
        }
        
    • 用Arrays.sort 进行二维数组排序

      要做二维数组排序,实际上就是自己有Comparator来定制

      • 只对二维数组的第一个数排序

        即将 二维数组

        [(1,9),(2,5),(19,20),(10,11),(12,20),(0,3),(0,1),(0,2)]

        经过排序后得到以下二维数组:

        [(0,3),(0,1),(0,2),(1,9),(2,5),(10,11),(12,20),(19,20)]

        public class Test {
            public static void main(String[] args) {
                int[][] arr = {{1,9},{2,5},{19,20},{10,11},{12,20},{0,3},{0,1},{0,2}};
                Arrays.sort(arr,new Comparator<int[]>(){
                    @Override
                    public int compare(int[] o1, int[] o2) {
                        return o1[0]-o2[0];
                    }
                });
            }
        }
        

        注意:这里的数组不需要设置成Interger[] [],因为int[] 本身就是引用而不是基本数据类型,所以泛型T是可以为 int[] 的

      • 二维数组按第一位数升序排序,若宽度一样按第二位数降序排序

        public class Test {
            public static void main(String[] args) {
                int[][] arr = {{1,9},{2,5},{19,20},{10,11},{12,20},{0,3},{0,1},{0,2}};
                Arrays.sort(arr,new Comparator<int[]>(){
                    @Override
                    public int compare(int[] o1, int[] o2) {
                        return o1[0]=o2[0]?o2[1]-o1[1]:o1[0]-o2[0];
                    }
                });
            }
        }
        

在leetCode上通过的代码:

如果是用的动态规划会超出时间限制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K2PQwzPU-1683776767666)(D:\Development\Typora\img\image-20230511114428889.png)]

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        //1.先处理数据,按宽度升序排序,宽度相同,高度降序排序
        // Arrays.sort(envelopes, (o1[],o2[])->{return o1[0]==o2[0]?o2[1]-o1[1]:o1[0]-o2[0];});
        Arrays.sort(envelopes, new Comparator<int[]>() {
        public int compare(int[] a, int[] b) {
            return a[0] == b[0] ? 
                b[1] - a[1] : a[0] - b[0];
        }
    });
        //2.处理完之后对高度找LIS
        int length=envelopes.length;
        int[] dp=new int[length];
        Arrays.fill(dp,1);
        for(int i=0;i<length;i++){
            for(int j=0;j<i;j++){
                if(envelopes[j][1]<envelopes[i][1]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
        }
        //3.找最大值
        int max=0;
        for(int k=0;k<length;k++){
            max=Math.max(max,dp[k]);
        }
        return max;
    }
}

所以要用到最长递增子序列的二分查找解法

class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        int n = envelopes.length;
        // 按宽度升序排列,如果宽度一样,则按高度降序排列
        Arrays.sort(envelopes, new Comparator<int[]>() 
        {
            public int compare(int[] a, int[] b) {
                return a[0] == b[0] ? 
                    b[1] - a[1] : a[0] - b[0];
            }
        });
        // 对高度数组寻找 LIS
        int[] height = new int[n];
        for (int i = 0; i < n; i++)
            height[i] = envelopes[i][1];

        return lengthOfLIS(height);
    }

    /* 返回 nums 中 LIS 的长度 */
    public int lengthOfLIS(int[] nums) {
        int piles = 0, n = nums.length;
        int[] top = new int[n];
        for (int i = 0; i < n; i++) {
            // 要处理的扑克牌
            int poker = nums[i];
            int left = 0, right = piles;
            // 二分查找插入位置
            while (left < right) {
                int mid = (left + right) / 2;
                if (top[mid] >= poker)
                    right = mid;
                else
                    left = mid + 1;
            }
            if (left == piles) piles++;
            // 把这张牌放到牌堆顶
            top[left] = poker;
        }
        // 牌堆数就是 LIS 长度
        return piles;
    }
}

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

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

相关文章

simulink电力系统仿真(1):电力系统库+(电力系统基础知识)

文章目录 基础知识仿真库搭建一个简单的电路 基础知识 ★三相交流电&#xff1a;三相电是三组幅值相等、频率相等、相位互相差120的交流电&#xff0c;由有三个绕组的三相发电机产生&#xff0c;是工业上常用的电源&#xff0c;可提供超过数千瓦或以上功率的电力。★电力系统暂…

看一看吧,面试真的卷......

大家好&#xff0c;最近有不少小伙伴在后台留言&#xff0c;今年面试实在是太卷了&#xff0c;不知道从何下手&#xff01; 不论是跳槽涨薪&#xff0c;还是学习提升&#xff01;先给自己定一个小目标&#xff0c;然后再朝着目标去努力就完事儿了&#xff01; 为了帮大家节约…

美术培训学校学生作品管理平台

本系统主要是为了美术培训学校和学生使用的&#xff0c;系统分为了前台和后台两部分&#xff0c;前台部分主要是展示美术培训学校的信息和让学生查看自己的课程信息服务的&#xff0c;后台主要是管理人员对系统进行管理使用的 前台部分功能 1. 网站首页&#xff0c;首页部分…

jar的反编译为java文件

目录 1、cfr工具下载 2、反编译指令 3、反编译说明 1、cfr工具下载 反编译插件工具比较多&#xff0c;但是我认为最后用的工具为cfr&#xff0c;基本能解决所有jar包&#xff0c;下载地址为&#xff1a;cfr官网 点击图中位置下载即可 2、反编译指令 来到你下载完成cfr目录下&…

蓝牙耳机哪个牌子好?数码粉总结学生平价蓝牙耳机推荐

蓝牙耳机这几年发展飞速&#xff0c;涌现了很多品牌和产品&#xff0c;越来越多的用户选择放弃有线耳机使用蓝牙耳机&#xff0c;学生们也不例外。前段时间看到网上很多学生在讨论蓝牙耳机哪个牌子好&#xff0c;我整理了五款口碑最好的学生平价蓝牙耳机推荐清单&#xff1a; 1…

cesium坐标系转换:经纬度地理坐标系 弧度地理坐标系 笛卡尔坐标 屏幕坐标之间的相互转换

参考了网上资料 核心就是 cesium 计算的地理数据用弧度坐标系表示&#xff08;很多公式计算出来的是弧度结果&#xff09;&#xff0c;我们采集的数据是经纬度表示的&#xff08;即我们正常的经纬度&#xff09;&#xff0c;围绕着cesium球体展示的用笛卡尔坐标系计算的其方位…

5月新刊 | MDPI版面费将全面上涨,还有哪些期刊可投?(新增多领域高性价比新刊, 含CCF-B/SSCI/EI)~

近期MDPI官方发布消息称&#xff0c;MDPI出版社旗下期刊的OA论文处理费 (APC), 价格将在2023年6月底上涨&#xff0c;超过90种OA期刊的APC涨幅在200-1200瑞士法郎 (折合人民币1560元-9300元)。 在MDPI出版的OA期刊中&#xff0c;有5种期刊处于2022年中国通迅作者发表OA论文数量…

junit如何在多模块项目中使用

文章目录 前言一、最简单的单元测试二、springboot多模块测试单元1.问题2.解决 总结 前言 相信后端的小伙伴对于junit测试应该不陌生,当我们写好了一些功能之后,由于不太放心是否会出现问题,我们会选择自测; 第一种 通过类似postman之类的,直接走接口测试第二种 由于构造数据…

Halcon 找到产品上 圆圈中的7个点

文章目录 1 关键实现环节演示2 完整代码 及 原图文章目录 1 关键实现环节演示2 完整代码 及 原图找到下图 圆圈中的7个点; 思路: 先找到圆圈外面的矩形, 再找到里面的圆圈, 最后找到圆圈里面的 圆点 1 关键实现环节演示 二值化 阈值分割止 之后,打散, read_image (im…

很多博主用Markdown格式文章?直呼真不错!

概述 Markdown 是一种轻量级标记语言&#xff0c;它可以使我们专注于写作内容&#xff0c;而不用过多关注排版&#xff0c;很多博主、作家等都用它来撰写文章~ 本文将给各位小伙伴介绍 Markdown 语法的使用&#xff0c;本篇文章索奇就是用的纯 markdown 语法来写的~ 标题 一级…

EKMA曲线绘制、MCM箱模型应用与O3形成途径、生成潜势、敏感性分析

目录 EKMA曲线及大气O3来源解析 MCM箱模型实践技术应用与O3形成途径、生成潜势、敏感性分析 一、 大气中O3形成知识基础、MCM和Atchem2原理及Linux系统安装 二、 MCM建模、数据输入、模型运行及结果输出 【讲解案例操作】 三、 O3形成途径、生成潜势及其敏感性分析 【讲解…

2023 谷歌I/O发布会新AI,PALM 2模型要反超GPT-4,一雪前耻!

文章目录 1 前言2 Google I/O 发布者大会3 PaLM 2模型3 Bard项目4 其他AI工具4.1 AI 图片编辑 Magic Editor4.2 Duet AI 办公4.3 Universal Translator 翻译工具4.4 Google 沉浸式导航4.5 Google 搜索引擎 5 讨论 1 前言 每年必看两大会&#xff0c;苹果发布会和谷歌发布会&am…

搭建AI虚拟数字人是一项复杂的任务,需要掌握多个技术领域的知识,包括机器学习、自然语言处理、计算机图形学等。

搭建AI虚拟数字人是一项复杂的任务&#xff0c;需要掌握多个技术领域的知识&#xff0c;包括机器学习、自然语言处理、计算机图形学等。以下是搭建AI虚拟数字人的一般步骤&#xff1a; 确定需求和功能&#xff1a;首先需要确定虚拟数字人的用途和需要实现的功能&#xff0c;例…

精心设计的ChatGPT网页版源码最聪明的镜像ChatGPT

demo软件园每日更新资源,请看到最后就能获取你想要的: 1.精心设计的 ChatGPT网页版源码 最聪明的镜像ChatGPT ChatGPT网页版源码 最聪明的镜像ChatGPT 支持GPT3.5turbo以及GPT4.0turbo的各种版本模型 精心设计的 UI&#xff0c;响应式设计&#xff0c;支持深色模式 极快的首…

【LeetCode】932. 漂亮数组

932. 漂亮数组&#xff08;中等&#xff09; 解法一&#xff1a;分治法 重点 这里给出两个定理&#xff1a; 如果 X,Y,Z 是漂亮数组&#xff0c;则 k * X b, k * Y b, k * Z b 一定也是漂亮数组&#xff1b;奇数 偶数 奇数。 因此不难证明&#xff0c;如果 2 * Y ≠ X…

vue+elementui写了一个图书管理系统

用vueelementui写了一个图书管理系统 转载自公号&#xff1a;java大师 目前是指一个纯前端的展示&#xff0c;后端还在开发中&#xff0c;前端接口是通过json-server模拟的 用到的技术栈 1、vue.js 2、elementui 3、json-server 4、axios 5、vue-router 动态路由 目录结构…

Java每日一练(20230511) 有效数字、重复元素II、类和子类

目录 1. 有效数字 &#x1f31f;&#x1f31f;&#x1f31f; 2. 存在重复元素 II &#x1f31f;&#x1f31f; 3. 设计学生类Student和它的一个子类Undergraduate &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专…

基于postgresql传统数据仓库搭建

目录 概述数仓选型对比当前数仓架构问题解决方案 架构设计数据仓库设计命名规范模型设计 PostgreSQL的安装数据仓库的建立创建数据库创建用户组创建用户用户加入到用户组创建模式模式授权用户收回函数的执行权限公开表的select权限动态sql函数集中处理函数 fdw实现数据抽取安装…

ChatGPT与Discord无缝接入指南,获取你的专属聊天机器人

ChatGPT与Discord无缝接入指南,获取你的专属聊天机器人 一、获取OpenAI API密钥。二、获取Discord Token三、注册GitHub,有账号的可以直接登录。四、在线开发环境搭建Replit五、注册cron-job.org本教程收集于: AIGC从入门到精通教程 ChatGPT和Discord都非常流行,而在Disco…

基于RK3588s人工智能大算力多网口工业网关交换机,可接5路千兆高清相机

RK3588/RK3588S AI BOX 功能接口说明 接口需求 功能定义与要求 备注 成本与目标 硬件设计争取全国产化&#xff0c; 632GB 版本 RK3588S PCBA 尺寸 146*102 丝印版本号 RK3588S AI BOX V1.0 EMMC 支持 SanDisk SDINBDA4-32/64/128G 4G-8GB 标配 8GB …