背包问题 总结详解

news2024/9/20 16:34:46

就是感觉之前 dp 的 blog 太乱了整理一下。


0-1 背包

例题:P1048

朴素算法

思路

对于一个物品,我们可以选,也可以不选。

我们用w_i表示第 i 件物品的重量,v_i表示第 i 件物品的价值。

考虑dp_{i,j}表示前 i 件物品放入容量为j的背包中的最大价值。

如果我们放不下第 i 件物品(把它打入冷宫),那么dp_{i,j}=dp_{i-1,j};否则入宫

然后发生了一场宫斗:dp_{i,j}=max(dp_{i-1,j},dp_{i-1,j-w_i}+v_i)

这就是0-1背包的状态转移方程。

Code
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn];
//w[i]表示第i个物品的重量,v[i]是价值 
int dp[maxn][maxm];
//dp[i][j]表示前i个物品放入容量为j的背包里的最大值
int main(){
	int n,m;
	cin>>n>>m;
	//n是物品个数,m是背包大小
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			if(j<w[i])//放不下第i个物品 
				dp[i][j]=dp[i-1][j];//打入冷宫 
			else//入宫 
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//然后是场宫斗
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
}

滚动数组优化

显然,朴素算法的二维数组太耗费空间了。我们可以进行优化。

观察一下二维数组的代码,注意到 i跟个摆设似的,没啥用途。(注意力惊人)

因为dp_{i,j}只跟dp_{i-1,j}有关系,所以我们没必要记录 i。

不妨我们来试一试优化(试试就逝世)

#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=w[i];j<=m;j++)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[m]<<endl;
	return 0;
}

乍一看没什么问题,实则喜提 0 分好成绩。

为什么呢?           dp_{j-w_i}更新的比dp_j早!

因此倒过来循环即可。

Code
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];//w是重量,v是价值
int main(){
	int n,m;//n是物品个数,m是背包容量
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=m;j>=w[i];i--)//这里注意了:由于如果正着循环,dp[j-w[i]]会比dp[i]更新的早,所以要倒过来循环 
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[m]<<endl;
	return 0;
}

完全背包

有N种物品和一个容量是M的背包,每种物品都有无限件可用。
第i种物品的体积是v[i],价值是w[i]。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

洛谷上找不到例题所以只能自己写了。

转 0-1 背包

完全背包和 0-1背包 的差别就是物品可以拿无限次。 所以考虑把完全背包转成 0-1背包。

我们可以想一下,物品有无限件,肯定是超过 M(背包容量)的

我们可以把每个物品拆分成\frac{M}{w_i}个小物品。这样就和 0-1 背包一样了。

拆分的代码:
int tot=0;//计算物品总数
for(int i=1;i<=n;i++){
	int a,b;//a是重量,b是价值
	cin>>a>>b;
	for(int j=1;j<=m/a;j++){
		w[++tot]=a;
		v[tot]=b;
	}
}
完整代码
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	int tot=0;//计算物品总数
	for(int i=1;i<=n;i++){
		int a,b;//a是重量,b是价值
		cin>>a>>b;
		for(int j=1;j<=m/a;j++){
			w[++tot]=a;
			v[tot]=b;
		}
	}
	for(int i=1;i<=tot;i++){
		for(int j=m;j>=w[i];j--)//倒着循环
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[m]<<endl;
	return 0;
}

完全背包自己的算法

这次我们不转 0-1背包。

由于至少放入一个,考虑最后一个放入的物品,其占用 w 的空间,价值是 v。

由于物品有无限多个,放入一个以后还是有无限多个,但是背包大小减少了 w。

问题转换为从前i个物品种选择一些物品放入j-w_i的背包中可以获得的最大价值。

由此推出状态转移方程:dp_{i,j}=max(dp_{i-1,j},dp_{i-1,j-k\times w_i}+k\times v_i)

我们用滚动数组,优化空间。和 0-1背包类似。

方程优化成这样:dp_j=max(dp_j,dp_{j-w_i}+v_i)

虽然它的状态转移方程和0-1背包的一样,但是它不用倒着循环,正着循环即可。

#include <bits/stdc++.h>
using namespace std;
//对于物品i,你可以把它打入冷宫(不选)或者让它入宫(选) 
//不过啊,这是无限背包,所以可以无限放 
//也就是说,你放完一个,还有无限个,但是呢背包的大小减少了w[i]
//因此问题转化为从前i个物品中选一些放入大小为j-w[i]的背包中可获得的最大价值
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=w[i];j<=m;j++)//这个正着循环即可
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 
	}
	cout<<dp[m]<<endl;
	return 0; 
}

转多重背包

没错,完全背包还可以转多重背包。不过建议读者先阅读下面的多重背包再看这个算法。

其实很简单。我们把多重背包里的s_i定义成\frac{m}{w_i}来求解就可以了。

状态转移方程:dp_{i,j}=max(dp_{i-1,j},dp_{i-1,j-k\times w_i}+k\times v_i)

最后是代码部分:

#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int k=0;k<=j/w[i];k++)
				dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
}

当然,由于多重背包的朴素算法的复杂度是\Theta (N\times M\times K)的,所以必须优化(参见下面多重背包部分)。

多重背包

例题:P1776

朴素算法

多重背包 0-1背包 的差别就是 0-1背包 里一个物品只有一件,但多重背包里一个物品有s[i]件。

所以,我们分类讨论:

  1. s[i]*w[i]>=m 转完全背包
  2. 转0-1背包。拿一个 k 去枚举数量,于是问题变成了一个重量为k\times w,价值为k\times v的物品取不取

来人!上代码!

#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],s[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i]>>s[i];
	for(int i=1;i<=n;i++){
		if(s[i]*w[i]>=m){//相当于完全背包(因为比背包容量m大) 
			for(int j=w[i];j<=m;j++)
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 
		}
		else{//否则转0-1背包 
			for(int j=m;j>=w[i];j--){
				for(int k=s[i];k>=0;k--){//枚举数量 
					if(j>=k*w[i])
						dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);
				}
			}
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

显然,时间复杂度不是一般的高,是二般的高

所以优化是必须的。接下来我们来看多重背包的两种优化。

多重背包的二进制优化

思路

我们想一下,我们当时用 k 从s_i枚举到0是否必要。

我们其实可以合并一些物品。

所以我们可以只枚举一些物品,通过这些物品互相合并,产生新的物品。比如:10=1+2+4+3

那为什么不拆成1+2+7呢?因为这样你就无法合成出4了。

那怎么枚举呢?这里要用到一点倍增的思想了。

给大家看一下局部代码:

int a,b,s;//a是重量,b是价值,s是数量
cin>>a>>b>>s;
//如何合成这些多余的物体
int k=1;
while(k<=s){
	w[++cnt]=k*a;//合成的重量
	v[cnt]=k*b;//合成的价值
	s-=k;//物品数相应减少
	k*=2;//翻倍
}
if(s){//没办法翻倍了
	w[++cnt]=s*a;
	v[cnt]=s*b;
    //剩下的自动合成
}

这样就OK了。

Code
//由于直接枚举取多少个会TLE
//所以我们进行二进制优化
//思考一下,其实我们没必要枚举0-s
//我们可以不断倍增:1,2,4,8,16,32,64,128...
//取这些数量个物品
//这样,通过不断组合可以组合出所有种类
//比如:10=1+2+4+3
//为什么有个3呢?
//因为正好剩个3了
//靠1,2,4,3可以组成0-10的所有数
//5=1+4 6=2+4 7=3+4 8=3+4+1 9=2+3+4 10=1+2+3+4
//所以在枚举时,我们不断倍增就可以了
//这样可以大大减少枚举量
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	int cnt=0;//记录新物体数(相当于优化后的n)
	for(int i=1;i<=n;i++){
		int a,b,s;
		//a是重量,b是价值,s是数量
		cin>>a>>b>>s;
		//接下来是重中之重
		//如何合成这些多余的物体
		int k=1;
		while(k<=s){
			w[++cnt]=k*a;//合成的重量
			v[cnt]=k*b;//合成的价值
			s-=k;//物品数相应减少
			k*=2;//翻倍
		}
		if(s){//没办法翻倍了
			w[++cnt]=s*a;
			v[cnt]=s*b;
			//剩下的自动合成
		}
	}
	//0-1背包
	for(int i=1;i<=cnt;i++){
		for(int j=m;j>=w[i];j--)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[m]<<endl;
	return 0;
}

我个人认为这个方法好懂,好写,比下面的单调队列优化好一些。

单调队列优化多重背包

单调队列优化的思想就是给拿完后剩余体积分类,并且剩余体积一定不大于w,不然就能再拿。

我们可以枚举剩余体积,代码如下:

for(int i=1;i<=n;i++)          //->物品个数
    for(int j=0;j<w;j++)       //->剩余体积
        for(int k=j;k<=m;k+=w) //->不同个数

到这应该不难理解。

接下来,我们把这些状态表示一下:

dp_0dp_wdp_{2\times w} (剩余体积 0)
dp_1 dp_{w+1} dp_{2\times w+1} (剩余体积 1)
......
dp_jdp_{w+j}dp_{2*w+j}  (剩余体积 j)

于是,我们把问题分成 j 类,每类就是一个单调队列。

dp_i=dp_j
dp_{j+w}=max(dp_j+v,dp_{j+w})
......
dp_{j+k\times w}=max(dp_j+k\times v,dp_{j+w}+(k-1)\times v,......,dp_{j+k\times w})

我们把v提出来,就是:

dp_j=dp_j
dp_{j+w}=max(dp_j,dp_{j+w}-v)+v
dp_{j+2*w}=max(dp_j,dp_{j+w}-v,dp_{j+2\times w}-2\times v)+2\times v
......
dp_{j+k\times w}=max(dp_j,dp_{j+w}-v,dp_{j+2\times w}-2\times v, ... ,dp_{j+k\times w}-k\times v)+k\times v

所以,我们用一个队列维护最大值的下标,每次入队dp_{j+k\times w}-k\times w即可。

但是由于我们要进行比较,所以我们还需要一个 pre 数组维护上一轮的队列就OK啦。

//单调队列优化的思想就是给拿完后剩余体积分类
//剩余体积一定<w,不然就可以再装
//0,1,2,3,......,w-1
//怎么枚举呢?看代码:
//for(int i=1;i<=n;i++)          ->物品个数
//    for(int j=0;j<w;j++)       ->剩余体积
//        for(int k=j;k<=m;k+=w) ->不同个数
//我们把这些状态表示一下:
//dp[0] dp[w] dp[2*w] ...... (剩余体积0)
//dp[1] dp[w+1] dp[2*w+1] ...... (剩余体积1)
//......
//dp[j] dp[w+j] dp[2*w+j] ...... (剩余体积j)
//我们把问题分为j类,每类就是一个单调队列
//dp[j]=dp[j]
//dp[j+w]=max{dp[j]+v,dp[j+w]}
//......
//dp[j+k*w]=max{dp[j]+k*v,dp[j+w]+(k-1)*v,......,dp[j+k*w]}
//我们稍微变换一下:
//dp[j]=dp[j]
//dp[j+w]=max{dp[j],dp[j+w]-v}+v
//dp[j+2*w]=max{dp[j],dp[j+w]-v,dp[j+2*w]-2*v}+2*v
//......
//dp[j+k*w]=max{dp[j],dp[j+w]-v,dp[j+2*w]-2*v,......,dp[j+k*w]-k*v}+k*v
//相当于把价值v挪到max函数外了
//所以说,我们每次入队dp[j+k*w]-k*w,维护最大值即可
//que维护的是dp[j+k*w]-k*w最大值的下标
//pre维护的是上一次的que
//每次比较一下即可 
#include <bits/stdc++.h>
using namespace std;
const int maxm=40005;
int dp[maxm],pre[maxm],que[maxm];//que是队列 
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int w,v,s;
		cin>>w>>v>>s;
		memcpy(pre,dp,sizeof(dp));
		for(int j=0;j<w;j++){//j个单调队列,j枚举的是拿完后剩下的重量(最多剩w-1) 
			int head=0,tail=-1;//头尾指针
			for(int k=j;k<=m;k+=w){//k是取(k-j)/w件物品的重量 
				if(head<=tail && k-s*w>que[head])
					head++;
				while(head<=tail && pre[que[tail]]-(que[tail]-j)/w*v<=pre[k]-(k-j)/w*v)
					tail--;
				if(head<=tail)
					dp[k]=max(dp[k],pre[que[head]]+(k-que[head])/w*v);
				que[++tail]=k;
			}
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

复习(混合背包)

Review

混合背包就是 0-1背包、完全背包、多重背包 的结合。

内容状态转移方程注意事项
0-1背包物品选/不选dp_j=max(dp_j,dp_{j-w_i}+v_i)倒着循环
完全背包物品无数件dp_j=max(dp_j,dp_{j-w_i}+v_i)正着循环
多重背包物品s_idp_j=max(dp_j,dp_{j-k\times w_i}+k\times v_i)优化
思路

混合背包还是输入w_i\: v_i\: s_i,分别表示重量,价值,数量。然后进行分类讨论:

  • s_i=1时,是 0-1背包。
  • s_i=0时,是完全背包。
  • 其他为多重背包。

然后套用以上的状态转移方程即可。

代码
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],s[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>m>>m;
	for(int i=1;i<=n;i++)
		cin>>w[i]>>v[i]>>s[i];
	for(int i=1;i<=n;i++){
		if(s[i]==1){//0-1背包 
			for(int j=m;j>=w[i];j--)
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
		else if(s[i]==0){//完全背包 
			for(int j=w[i];j<=m;j++)
				dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
		else{//多重背包(朴素),转0-1背包 
			for(int j=m;j>=w[i];j--){
				for(int k=1;k<s[i] && k*w[i]<=m;k++)
					dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]);
			} 
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}

分组背包

例题:P1757

思路

和 0-1背包 类似,只用一个二维数组存储每个组的解就行了。每组按 0-1 背包的解法做就ok了。

代码

#include <bits/stdc++.h>
using namespace std;
vector<int> w[maxn],v[maxn];
int dp[maxm];
int main(){
	for(int i=0;i<maxn;i++){
        w[i].push_back(0);
        v[i].push_back(0);
    }
	int m,n;
	cin>>m>>n;
	int kind=0;//种类数 
	for(int i=1;i<=n;i++){
		int a,b,c;//a是重量,b是价值 
		cin>>a>>b>>c;//c是种类 
		w[c].push_back(a);
		v[c].push_back(b);
		kind=max(kind,c);
	}
	for(int i=1;i<=kind;i++){
		for(int j=m;j>=0;j--){//倒着循环(0-1)背包 
			for(int k=1;k<(int)w[i].size();k++){
				if(j>=w[i][k])
					dp[j]=max(dp[j],dp[j-w[i][k]]+v[i][k]);
			} 
	    }
	}
	cout<<dp[m]<<endl;
	return 0;
}

结尾

这篇 blog 用了我整整 1 天的时间整理,主要因为之前的背包的 blog 太散了,之后还会整理线性 dp 和区间 dp 的blog。看在我写了 7k+ 字的份上,点个赞再走吧。

友情提醒:虽然模板都是正确的,但直接提交会让你喜提 0 分(里面的 maxn 我都没有定义)

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

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

相关文章

时间复杂度计算 递归(solve2 后续)

原帖 最近校内比较忙&#xff0c;更新缓慢&#xff0c;致歉。 这里函数每次都需要遍历 h h h 和 m m m 之间的数&#xff08;复杂度 O ( n ) O(n) O(n)&#xff09;&#xff0c;所以和 solve1 略有不同。仍然假设 T ⁡ ( n ) \operatorname{T}(n) T(n) 表示 m − h 1 n…

【C++二叉树】606.根据二叉树创建字符串

606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 图文分析&#xff1a; 代码实现&#xff1a; 代码说明&#xff1a; 1、前序遍历方式&#xff1a;根-左子树-右子树。 2、题目要求将二叉树转换为字符串输出&#xff0c;所以定义了一个string对象str。 3…

MySQL —— 视图

概念 视图是一张虚拟的表&#xff0c;它是基于一个或多个基本表或其他视图的查询结果集。 视图本身不存储数据&#xff0c;而是通过执行查询来动态生成数据&#xff0c;用户可以像操作普通表一样使用视图来进行查询更新与管理等操作。 视图本身也不占用物理存储空间&#xf…

网络安全学习(五)Burpsuite

经过测试&#xff0c;发现BP需要指定的JAVA才能安装。 需要的软件已经放在我的阿里云盘。 &#xff08;一&#xff09;需要下载Java SE 17.0.12(LTS) Java Downloads | Oracle 1.2023版Burp Suite 完美的运行脚本的环境是Java17 2.Java8不支持 看一下是否安装成功&#xff0c…

开源AI应用安全指导框架 — OWASP AI Exchange

在当今信息化迅猛发展的时代&#xff0c;网络专业人士正竞相提升人工智能&#xff08;AI&#xff09;安全领域的专业技能。随着这一趋势的推进&#xff0c;他们的企业也在快速地引入各类AI工具、平台、应用程序和服务&#xff0c;业界也相应涌现出众多资源&#xff0c;以协助从…

电梯电动车检测-目标检测数据集(包括VOC格式、YOLO格式)

电梯电动车检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1qRMdF08Jinx_5CRa3al24A?pwd3twc 提取码&#xff1a;3twc 数据集信息介绍&#xff1a; 共有 5347 张图像和一一对应的标注文件 …

web基础—dvwa靶场(五)File Upload

File Upload(文件上传) 上传的文件对 web 应用程序来说是一个巨大的风险&#xff0c;许多攻击的第一步是上传攻击代码到被攻击的系统上&#xff0c;然后攻击者只需要找到方法来执行代码即可完成攻击。也就是是说&#xff0c;文件上传是攻击者需要完成的第一步。 不受限制的文件…

c#中给winform定义快捷键的几种方式

快捷键的使用在日常的开发中频率比较高&#xff0c;这里总结了最常见的各种快捷键的设置方式&#xff0c;需要的时候大家直接照抄就可以了&#xff0c;不用再去查询如何实现了。 文章目录 一、按钮快捷键二、菜单快捷键三、窗体快捷键四、全局快捷键1、重写ProcessCmdKey2、使…

C++内存管理详解:各类变量的存储区域

在C中&#xff0c;变量的存储位置取决于它们的类型和生命周期。那么不同的各个变量究竟存储在哪个区域呢&#xff1f; 1.不同类型的变量 我们首先从变量类型的不同来说明&#xff1a; 1. 全局变量和静态变量 - 存储区&#xff1a;全局/静态区&#xff08;静态区&#xff0…

深度学习----------------------文本预处理

目录 文本预处理读取数据集词源化词表该部分总代码该部分总代码 整合所有功能该部分总代码 文本预处理 文本预处理&#xff1a;把文本当作一个时序序列 将解析文本的常见预处理步骤。 这些步骤通常包括&#xff1a; ①将文本作为字符串加载到内存中。 ②将字符串拆分为词元&…

Redis的存储原理和数据模型

一、Redis是单线程还是多线程呢&#xff1f; 我们通过跑redis的代码&#xff0c;查看运行的程序可以得知&#xff0c;Redis本身其实是个多线程&#xff0c;其中包括redis-server&#xff0c;bio_close_file&#xff0c;bio_aof_fsync&#xff0c;bio_lazy_free&#xff0c;io_t…

Python如何导入自定义包?

在 Python 中&#xff0c;导入包是日常开发的基础操作之一。Python 通过其模块化设计&#xff0c;使得代码可以组织成模块和包&#xff0c;提升了代码的复用性和可维护性。而当开始构建复杂的Python项目时&#xff0c;通常会发现将代码组织在各种模块和包中是非常有帮助的。自定…

【C++】list常见用法

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;C从小白到高手 &#x1f339;往期回顾&#x1f339;&#xff1a;[C]vector常见用法 &#x1f516; 流水不争&#xff0c;争的是滔滔不息。 文章目录 一、list的介绍li…

JVM 调优篇6 可视化性能监控工具-JVisual VM

一 Visual VM 1.1 概述 Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。 它集成了多个JDK命令行工具&#xff0c;使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo)&#xff0c;监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat…

Celery的使用

Celery 一、Celery概述1. 特点:2. celery组成3. 安装与使用4. 邮箱配置二、Celery的使用实操——发送邮件1. 安装2. 配置一、Celery概述 1. 特点: 2. celery组成 配置任务队列Broker,采用redis保存要执行的任务队列 Client:任务的发出者 Worker:任务的处理者 3. 安装与使用…

从0-1 用AI做一个赚钱的小红书账号(不是广告不是广告)

大家好&#xff0c;我是胡广&#xff01;是不是被标题吸引过来的呢&#xff1f;是不是觉得自己天赋异禀&#xff0c;肯定是那万中无一的赚钱天才。哈哈哈&#xff0c;我告诉你&#xff0c;你我皆是牛马&#xff0c;不要老想着突然就成功了&#xff0c;一夜暴富了&#xff0c;瞬…

信奥初赛解析:1.2-计算机系统的基本结构

目录 知识要点 一、概述 二、计算机硬件系统 &#xff08;一&#xff09;处理器 &#xff08;二&#xff09;存储器 (1)内存储器 (2)外存储器 ①固态硬盘存储器 ②机械硬盘存储器 ③闪存 (三)输入设备 (四)输出设备 (五)总线结构 (六)主要的性能指标 1.字长 2.运…

计算机毕业设计 网上书店系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

JVM OutOfMemoryError 与 StackOverflowError 异常

目录 前言 堆溢出 虚拟机栈和本地方法栈溢出 方法区溢出 前言 JVM规范中规定, 除了程序计数器之外, 其他的运行时数据区域, 例如堆栈, 方法区, 都会出现OutOfMemoryError异常. 那么到底是怎么样的代码, 才会引起堆溢出, 栈溢出, 或者是方法区的溢出呢? 如果遇到了又该如何…

书生大模型全链路开源体系,学习

优点 书生浦语开源大模型&#xff0c;是一个开源的大模型&#xff0c;大家可以一起学习 还有配套的教学视频&#xff0c;很快就能上手&#xff0c;而且还奖励算力&#xff0c;可以直接训练&#xff0c;讨论学习&#xff0c;非常nice。 教学视频 书生浦语大模型全链路开源开…