背包问题——01背包|完全背包

目录

前言&背包问题的历史

 01背包

 1、题目

2、暴力解01背包

 Ⅰ、代码

3、动态规划解01背包

Ⅰ、二维dp数组解01背包

1)dp数组的含义

 2)递推公式

 3)dp数组的初始化

 4)遍历顺序的讨论

 5、代码

 Ⅱ、一维数组解01背包

 1)一维数组|滚动数组

 2)一维数组的含义及递推公式

 3)一维数组的初始化

 4)遍历一维数组

5)遍历顺序的讨论

 6)代码

 完全背包

1、题目

 2、思路

 3、遍历顺序的讨论

 4、代码

题目推荐


前言&背包问题的历史

背包问题(Knapsack problem)是一种组合优化的NP完全问题(NP完全问题,是世界七大数学难题之一。 NP的英文全称是Non-deterministic Polynomial的问题,即多项式复杂程度的非确定性问题。简单的写法是 NP=P?,问题就在这个问号上,到底是NP等于P,还是NP不等于P)。

背包问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。

相似问题经常出现在商业、组合数学,计算复杂性理、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkle和Hellman提出的。

背包问题已经研究了一个多世纪,早期的作品可追溯到1897年   数学家托比亚斯·丹齐格(Tobias Dantzig,1884-1956)的早期作品  ,并指的是包装你最有价值或有用的物品而不会超载你的行李的常见问题。

 具体可看背包问题_百度百科 (baidu.com)

可以看到上面的图,背包问题还是很有很多种,但其实我们非竞赛人员只需要学会01背包、完全背包,最多再加上点多重背包。博主只讨论这三种背包问题。

我们都知道背包问题是动态规划经典问题,但为什么不可以使用贪心思维来解决呢?我举个简单例子。

 

 如果上面我们使用贪心思想总是去装 价值 / 物品大小 大的,即最多装价值为的16的物品,还剩余容量为3,无法再装下其他物品,这也是为什么不能用贪心去做,无法最大化的利用背包空间。

 01背包

 1、题目

一共有N件物品,第i(i从1开始)件物品的重量为w[i],价值为v[i]。在总重量不超过背包承载上限W的情况下,求能够装入背包的最大价值是多少?

2、暴力解01背包

 说起背包就会想到dp,使用动态规划的方法写,其实这也可以使用回溯算法暴力求解,大家不常用回溯算法暴力求解是因为每件物品都存在装入和不装入两种情况,总的时间复杂度是O(2ᴺ), 这是不可取的。但我们也可以了解了解怎么暴力求解背包问题。

还是使用以上面的例子试一试:

5 10
2 5 4 2 3
6 3 5 4 6

 Ⅰ、代码

import java.util.Scanner;

public class Main {
    static int SetCapacity=0;//背包的初始容量
    static int MaxCapacity=0;//使用了背包多少容量
    static int MaxValue=0;//最大价值
    static void method(int[][] arr,int k,int capacity,int value){
        for (int i=k;i<arr.length;i++){
            if (SetCapacity-capacity>=arr[i][0]){//判断当前背包的剩余容量是否可以装下第i个物品
                capacity+=arr[i][0];
                value+=arr[i][1];
                method(arr,i+1,capacity,value);
                //回溯
                capacity-=arr[i][0];
                value-=arr[i][1];
            }else{//如果不行,则跳过第i个物品,继续递归后面的物品,看是否可以装下。
                if (capacity>=SetCapacity)//如果背包装不下了,则退出循环,返回上一次递归
                    break;
                method(arr,i+1,capacity,value);
            }
        }
        if (value>MaxValue){
            MaxValue=value;
            MaxCapacity=capacity;
        }
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        SetCapacity=sc.nextInt();
        int[][] arr=new int[n][2];
        for (int i=0;i<n;i++){//物品大小
            arr[i][0]=sc.nextInt();
        }
        for (int i=0;i<n;i++){//物品价值
            arr[i][1]=sc.nextInt();
        }
        method(arr,0,0,0);
        System.out.println(MaxCapacity+" "+MaxValue);//9 17
    }
}

3、动态规划解01背包

Ⅰ、二维dp数组解01背包

1)dp数组的含义

可能很多人会写动态规划的题目,会写01背包,但是很多人却不会去注重思考dp数组的含义,其代表着什么?

01背包的dp数组含义是:dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少

不懂可以看看下面图,仔细想想。

 2)递推公式

dp[i][j]=Max( dp[i-1][j] , dp[i-1][ j-wᵢ ]+vᵢ )

dp[i][j]我们在上面知道 这 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。此时我们应该考虑下标为i物品是否放入背包(先要考虑j-wᵢ是否大于等于0,即此时是否能够放入物品i ),于是就有两种情况: 或者 不放

不放物品i就是dp[i-1][j]嘛,放物品i就是容量为j的背包需要留出这个物品i的容量才可以放物品i,即dp[i-1][ j-wᵢ ],而这又是下标为 0到 i-1 的物品里任意取,放进容量为 j-wᵢ 的背包,最大的价值总和。然后再由 dp[i-1][ j-wᵢ ]+vᵢ  就是dp[i][j]放物品 i  的最大价值。

然后 取 Max( dp[i-1][j] , dp[i-1][ j-wᵢ ]+vᵢ )  二者的最大值。

 3)dp数组的初始化

 我们由上面知道了求 dp[i][j] 需要知道 dp[i-1][j]和 dp[i-1][ j-wᵢ ]+vᵢ ,即需要先知道上图红色箭头所指的位置,这样我们便清楚知道了,dp数组应该如何初始化,即绿色箭头所指的列和行。

初始化如下图:

 dp数组初始化了,递推公式我们也知道了,后面这个数组自然而然也可以求出来了。

 4)遍历顺序的讨论

 上面我们得出dp数组是先遍历物品,再遍历背包容量,那可不可以先遍历背包容量再遍历物品,答案是可以的,但此时 dp[j][i] =Max( dp[ j-wᵢ ][ i-1 ]+vᵢ ,dp[ j ][ i-1 ] )

而此时 dp数组的含义就是 容量为 j 的背包随机放 0-i 之间的物品,所放物品的最大价值。

 

 5、代码

 以先遍历物品,在遍历背包为例

public class Main {
    public static void main(String[] args) {
        int[] weight = {1,3,4};
        int[] value = {3,4,6};
        int bagSize = 4;
        method(weight,value,bagSize);
    }

    /**
     * 动态规划获得结果
     * @param weight  物品的重量
     * @param value   物品的价值
     * @param bagSize 背包的容量
     */
    public static void method(int[] weight, int[] value, int bagSize){

        // 创建dp数组
        int goods = weight.length;  // 获取物品的数量
        int[][] dp = new int[goods][bagSize + 1];

        // 初始化dp数组
        // 创建数组后,其中默认的值就是0
        for (int j = weight[0]; j <= bagSize; j++) {
            dp[0][j] = value[0];
        }

        // 填充dp数组
        for (int i = 1; i < weight.length; i++) {
            for (int j = 1; j <= bagSize; j++) {
                if (j < weight[i]) {
                    /**
                     * 当前背包的容量都没有当前物品i大的时候,是不放物品i的
                     * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                     */
                    dp[i][j] = dp[i-1][j];
                } else {
                    /**
                     * 当前背包的容量可以放下物品i
                     * 那么此时分两种情况:
                     *    1、不放物品i
                     *    2、放物品i
                     * 比较这两种情况下,哪种背包中物品的最大价值最大
                     */
                    dp[i][j] = Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] + value[i]);
                }
            }
        }

        // 打印dp数组
        for (int i = 0; i < goods; i++) {
            for (int j = 0; j <= bagSize; j++) {
                System.out.print(dp[i][j] + "\t");
            }
            System.out.println();
        }
    }
}

 输出

0	3	3	3	3	
0	3	3	4	7	
0	3	3	4	7	

 Ⅱ、一维数组解01背包

 1)一维数组|滚动数组

 我们在上面知道了  二维数组的递推公式是 dp[i][j]=Max( dp[i-1][j] , dp[i-1][ j-wᵢ ]+vᵢ )。

即dp[i][j] 的值只与上一层的左上方和正上方有关。换句话说只与上一层有关。那我们是不是可以将二维dp数组降为一维dp数组呢?

答案是可以的。

我们可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][ j -wᵢ ] + vᵢ );

与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。

这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。

 2)一维数组的含义及递推公式

 而此时一维数组dp[j]的含义就是  容量为j的背包所背的最大价值。

那么如何推导dp[j]呢?

dp[j]可以通过dp[ j - wᵢ ]推导出来,dp[j - wᵢ ]表示容量为j - wᵢ 的背包所背的最大价值

dp[j - wᵢ ] + vᵢ 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])

此时dp[j]有两个选择,一个是不放物品i ,取自己 dp[j] 相当于 二维dp数组中的dp[i-1][j],,一个是放物品i 取dp[j - wᵢ ] + vᵢ然后求两者最大值,所以递归公式为:

dp[j] = max(dp[j], dp[j - wᵢ] + vᵢ);

 3)一维数组的初始化

关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱

dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j],那么dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。

那么dp数组除了下标0的位置,初始化为0,其他下标应该初始化多少呢?

看一下递归公式:dp[j] = max(dp[j], dp[j - wᵢ ] + vᵢ );

dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。

这样才能让dp数组在递归公式的过程中取到最大的价值,而不是被初始值覆盖了

 4)遍历一维数组

 我们由上面知道了递推公式为 dp[j] = max(dp[j], dp[j - wᵢ ] + vᵢ )。虽然它是一维数组,但和我们二维递推数组还是很想的,所以想想我前面说的话:

dp[i][j] 的值只与上一层的左上方和正上方有关。

所以我们遍历的时候 应该先遍历物品,再遍历背包容量并且背包是从大到小倒序遍历。

 //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }

 还是这张图,如果我们顺序遍历背包,那么后面的值就会被影响,这会导致物品i被重复多次加入举一个例子:物品1的重量 w = 1,价值 v= 3

如果正序遍历

dp[1] = dp[1 - w ] + v = 3

dp[2] = dp[2 - w ] + v = 6

此时dp[2]就已经是6了,意味着物品1,被放入了两次(这也违背了01背包每个物品只能放一次的规定,或者说这是完全背包),所以不能正序遍历。

而从后往前循环,每次取的状态不会和之前取的状态重合,这样每种物品就只取了一次。

5)遍历顺序的讨论

那这个一维数组的两个for循环是否可以颠倒呢?

答案是不可以的。

因为一维dp的写法,背包容量一定是要倒序遍历(原因上面已经讲了),如果遍历背包容量放在上一层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个符合当前容量的最大价值的物品

倒序遍历的原因是,本质上还是一个对二维数组的遍历,并且右下角的值依赖上一层左上角的值,因此需要保证左边的值仍然是上一层的,从右向左覆盖。

 可以看下面循环顺序颠倒的代码,发现 个dp[j]就只会放入一个物品

public class Main {
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {3, 4, 6};
        int bagWight = 4;
        method(weight, value, bagWight);
    }

    public static void method(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagWeight + 1];
        //遍历顺序:先遍历背包容量,再遍历物品,背包中就只会放一个物品
            for (int j =bagWeight; j >=0; j--){
                for (int i = 0; i < wLen&&j >= weight[i]; i++){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for (int j = 0; j <= bagWeight; j++){
            System.out.print(dp[j] + " ");
        }
    }
}

 输出

0 3 3 4 6 

 6)代码

public class Main {
    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {3, 4, 6};
        int bagWight = 4;
        method(weight, value, bagWight);
    }

    public static void method(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagWeight + 1];
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for (int j = 0; j <= bagWeight; j++){
            System.out.print(dp[j] + " ");//0 3 3 4 7
        }
    }
}

 完全背包

1、题目

有N种物品和一个容量为V的背包,每种物品都有无限件可用(这也是与01背包的区别)

第i种物品的体积是c,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。

 2、思路

这个问题非常类似于01背包问题,所不同的是每种物品有无限件,也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……取[V/c]件等很多种(我们可以想象01背包就是取0件或者1件,所以完全背包其实就是01背包转换过来的)。如果仍然按照解01背包时的思路,令f[v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程,像这样:

dp[j]=Max( dp[j] , dp[j-c]+w)  或者

dp[j]=Max( dp[j] , dp[j-k*c]+k*w)(0<=k*c<=v)(k是物品存放的件数)

 3、遍历顺序的讨论

看了前面,我们知道01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量,而且背包容量还要是倒序(因为顺序的话就会将一个物品多次放入背包中)。

那么完全背包可以颠倒顺序吗?(指纯完全背包)

答案是可以的。

上面我们说的因为顺序的话就会将一个物品多次放入背包中,而完全背包每个物品有无限个,所以完全背包不需要倒序,顺序即可。而01背包一维数组因为只能倒序,所以先遍历背包容量,再遍历背包,背包中就只会放一个物品。但是完全背包解决了顺序倒序的问题,可以使用顺序了,那它先先遍历背包容量,再遍历背包便也没问题了。

 为了方便 理解,可以下面画的两个二维dp数组的图

先物品后背包容量

 先背包容量再物品

 看了看到,不管怎么遍历,都可以由前面的值得到正确的dp[i][j],不会受到影响

 4、代码

public class Main {
    //先遍历物品,再遍历背包
    static void testCompletePack(){
        int[] weight = {1, 3, 4};
        int[] value = {3, 4, 6};
        int bagWeight = 4;
        int[] dp = new int[bagWeight + 1];
        for (int i = 0; i < weight.length; i++){ // 遍历物品
            for (int j = weight[i]; j <= bagWeight; j++){ // 遍历背包容量
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        for (int maxValue : dp){
            System.out.print(maxValue + " ");
        }
    }

    //先遍历背包,再遍历物品
    static void testCompletePackAnotherWay(){
        int[] weight = {1, 3, 4};
        int[] value = {3, 4, 6};
        int bagWeight = 4;
        int[] dp = new int[bagWeight + 1];
        for (int i = 1; i <= bagWeight; i++){ // 遍历背包容量
            for (int j = 0; j < weight.length; j++){ // 遍历物品
                if (i - weight[j] >= 0){
                    dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
                }
            }
        }
        for (int maxValue : dp){
            System.out.print(maxValue + " ");
        }
    }
    public static void main(String[] args) {
       testCompletePack();//0 3 6 9 12 
        System.out.println();
       testCompletePackAnotherWay();//0 3 6 9 12 
    }
}

题目推荐

2. 01背包问题 - AcWing题库

494. 目标和 - 力扣(Leetcode)

3. 完全背包问题 - AcWing题库

 518. 零钱兑换 II - 力扣(Leetcode)

 4877. 最大价值 - AcWing题库

                                                       

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#调用C++封装的SDK库(dll动态库)——上

C#调用C封装的SDK库(dll动态库)——上 一、C封装库 通过前几篇文章&#xff0c;我们封装了C的动态DLL库&#xff0c;有Qt版的&#xff0c;有C版的&#xff0c;当然还有介绍了Pimpl模式在SDK封装中的使用&#xff1a; Qt创建SDK VS创建SDK Pimple在SDK封装中的应用 但是&a…

RabbitMQ入门

AMQP AMQP(Advanced Message Queuing Protocol,高级消息队列协议) 是进程之间传递异步消息的网络协议。 AMQP工作过程 发布者(Publisher)发布消息(Message),经过交换机(Exchange)&#xff0c;交换机根据路由规则将收到消息分发给交换机绑定的队列(Queue)&#xff0c;最后AM…

二维数组的总结

一、时间复杂度和空间复杂度 时间复杂度和空间复杂度是衡量算法效率的两个重要指标。时间复杂度是指算法执行所需的时间&#xff0c;而空间复杂度是指算法执行所需的内存空间。 计算时间复杂度和空间复杂度需要分析算法中各个操作的执行次数和内存使用情况。具体的计算方法可以…

亚马逊、ebay、temu如何提升产品点击率?测评自养号解析

产品点击率对于店铺销售额的影响至关重要&#xff0c;尤其是在竞争越来越激烈的市场环境中&#xff0c;想要有销量和转化&#xff0c;提高产品listing点击率成为了非常关键的一环。 1. 产品主图 顾客浏览产品时&#xff0c;第一眼看到的就是主图&#xff0c;一张优质的主图更容…

CSDN博客编写教程

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

UniLM模型简单介绍

目录 一、概要 二、深入扩展 2.1 预训练任务 2.2 模型精调 一、概要 如果将基于Transformer的双向语言模型&#xff08;如BERT模型中的掩码语言模型&#xff09;与单向的自回归语言模型&#xff08;如BART模型的解码器&#xff09;进行对比&#xff0c;可以发现&#xff0c…

springboot+vue职称评审管理系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的职称评审管理系统。项目源码请联系风歌&#xff0c;文末附上联系信息 。 目前有各类成品java毕设&#xff0c;需要请看文末联系方式 …

[Java]监听器(Listener)

过滤器&#xff08;Filter&#xff09;https://blog.csdn.net/m0_71229255/article/details/130246404?spm1001.2014.3001.5501 一 : Listener监听器简述 监听器就是监听某个对象的的状态变化的组件 监听器的相关概念&#xff1a; 事件源&#xff1a; 被监听的对象 ----- 三…

(补)4.13每日一题

给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 题目连接&#xff1a;https://leetcode.cn/problems/longest-substring-without-repeating-characters/ 解题 开始我把这个题目想简单了&#xff0c;我想的是输入一个字符串&#xff0c;从第一…

【系统集成项目管理工程师】项目整体管理

&#x1f4a5;十大知识领域&#xff1a;项目整体管理 项目整体管理包括以下 6 个过程: 制定项目章程定项目管理计划指导与管理项目工作监控项目工作实施整体变更控制结束项目或阶段过程 一、制定项目章程 制定项目章程。编写一份正式文件的过程&#xff0c;这份文件就是项目章程…

pushmall推贴共享电商2023年4月计划

Pushmall推贴共享电商2023年4月计划 2023年 二月份优化完成 1、商圈套餐卡&#xff1a;商品、优惠券、活动优化&#xff1b; 2、会员预充值一卡通&#xff1a;指定商家会员卡充值优惠&#xff1b; 3、商家海报&#xff1a;店铺海报、商品海报、商圈卡海报优化。 4、首页重新布…

MLCC周期性分析:当前时点处于周期反转前夜

MLCC是电子工业大米&#xff0c;供需波动导致行业成周期性波动 MLCC是最常用的被动元器件之一&#xff0c;终端下游涵盖消费电子、家电、汽车、通信等。在5g、汽车电子、智能硬件的推动下&#xff0c;MLCC行业需求稳步增长。供给端来看&#xff0c;中国大陆厂商合计市场份额不…

数据要素化全面提速,数据复制将迎来春天?

数据复制市场将迎来真正的春天&#xff1f; 目前看的确如此。近日&#xff0c;国家发改委密集发文&#xff0c;从产权、分配、流通、安全等多个角度解读“数据二十条”&#xff08; 《中共中央国务院关于构建数据基础制度更好发挥数据要素作用的意见》&#xff0c;简称“数据二…

Bots攻击威胁石油石化企业 瑞数动态安全实现从“人防”到“技防”

近日&#xff0c;中国石油石化企业信息技术交流大会暨油气产业数字化转型高峰论坛在京召开。本届大会由中国石油学会、中国石油、中国石化、中国海油、国家管网、国家能源、中国中化、中国航油、延长石油、中国地质调查局等单位共同主办。 作为我国石油石化行业的盛会&#xf…

什么是设计模式?

目录 常见的设计模式 创建型模式 结构型模式 行为型模式 总结 设计模式&#xff08;Design Pattern&#xff09;是一些被认为是最佳实践的面向对象编程经验的总结&#xff0c;它们提供了解决特定场景问题的可复用方案。设计模式可以加速开发过程并提高代码质量和可读性&…

GFD233A 3BHE022294R0103

GFD233A 3BHE022294R0103 ABB KUC321AE PLC模块 HIEE300698R0001 KU C321 AE01 ABB KUC711 3BHB004661R0001 高压变频模块 KUC711AE ABB KUC755AE105 3BHB005243R0105 驱动控制系统模块 KUC755 ABB KUC755AE106 3BH005243R006 控制系统模块 KU C755 AE 106 ABB LDGRB-01 3BSE01…

【C语言】基础语法1:变量和数据类型

下一篇&#xff1a;运算符和表达式 ❤️‍&#x1f525;前情提要❤️‍&#x1f525;   欢迎来到C语言基本语法教程   在本专栏结束后会将所有内容整理成思维导图&#xff08;结束换链接&#xff09;并免费提供给大家学习&#xff0c;希望大家纠错指正。本专栏将以基础出发…

知乎版ChatGPT「知海图AI」加入国产大模型乱斗,称效果与GPT-4持平

“2023知乎发现大会”上&#xff0c;知乎创始人、董事长兼CEO周源和知乎合作人、CTO李大海共同宣布了知乎与面壁智能联合发布“知海图AI”中文大模型。 周源据介绍&#xff0c;知乎与面壁智能达成深度合作&#xff0c;共同开发中文大模型产品并推进应用落地。目前&#xff0c;知…

vue 报错 error:03000086:digital envelope routines::initialization error解决方案

目录 1. 引言: 2. 更换版本出现问题: 3. 出现原因: 4. 解决办法: -> 4. 1 删了 再换回16.15版本 -> 4.2 指令修改(好使) ---> 4.2.1效果如图 -> 4.3 其他指令就别试了 压根不好使 1. 引言: npm出现问题 , 卸载后 装了个新node 18.15版本 2. 更换版本…

JavaScript【三】JavaScript中的数组

文章目录 &#x1f31f;前言&#x1f31f;数组&#x1f31f;声明&#xff1a;&#x1f31f; 隐式创建&#xff1a;&#x1f31f; 实例化构造函数&#xff1a; &#x1f31f; 注意&#xff1a;一个值为数组的长度。&#x1f31f; 访问&#xff1a;&#x1f31f; 遍历&#xff1a…