当前位置:网站首页>背包问题-动态规划-理论篇
背包问题-动态规划-理论篇
2022-08-02 14:10:00 【老顽固也可爱】
01背包
现在有n个物品,每个物品最多取一次, 有一个背包,它所装物品重量之和不能超过W,第i个物品它的重量是wi,价值是vi, 现在的问题是,在不超过背包容量的限制下,最多能获得的物品价值之和是多少。
(1<=n<= 2000, 0<= W<= 2000, vi>0, wi> 0)
解决方案1
我们先考虑遍历所有的方法,如果物品个数为n,那么总的方案数就是2 ^ n种(选或者不选),我们可以用dfs搜索实现。
//递归的解决的代码,需要用记忆化搜索
int dfs(int i, int j)
{
if(i==0||j<0) return 0;
if(dp[i][j]!=-1) return dp[i][j];
dp[i][j]= dfs(i-1,j);
if(j>=w[j])dp[i][j]=max(dp[i][j],dfs(i-1,j-w[i]));
return dp[i][i];
}
/*初始化dp数组为-1, answer=dfs(n, W). 尝试把递归解决动态规划和搜索进行对比。*/
但是n最大能到达2000,显然2 ^ 2000的复杂度难以接受,我们能不能得到一种时间复杂度更小的方法呢?
解决方案2
我们考虑用dp[i][j]表示已经处理了前i个物品,现在背包里的物品重量之和不大于j的情况下所能获得的最大物品价值之和。
状态转移方程:
dp[i][j] = max(dp[i-1][j], dp[i- 1][j-wi]+vi)
dp[i-1][ij]:表示不选第i个物品。
dp[i- 1][j-wi]+vi:表示选第i个物品。
//递推解决
for(int i=0;i<=W;i++) dp[0][i]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=W;j++)
{
d[i][j]= dp[i-1][j];
if(j>=w[i])
dp[i][j]=max(dp[i][j],dp[i-1][j-w[i]]+v[j]);
}
//answer=dp[n][W]
空间优化
使用滚动数组: cur表示当前层,last表示上一层
for(int j=0;j<=W;j++)cur[j]=0;
for(int i=1;i<=n;j++)
{
for(int j=0;j<=W;j++)
last[j]=cur[j],cur[j]=0;
for(int j=0;j<=W;j++)
{
cur[j]=last[j];
if(j>=w[i])
cur[j]=max(cur[j],last[j-w[i]);
}
}
// answer = cur[W]
无限背包
现在有n个物品,每个物品能取任意次,有一个背包,它所装物品重量之和不能超过W,第i个物品它的重量是wi,价值是vi, 现在的问题是,在不超过背包容量的限制下,最多能获得的物品价值之和是多少。
(1<=n<= 2000, 0<= W<= 2000, vi>0, wi> 0)
解决方案1
把无限背包问题转换成01背包问题,第i种物品最多有W / wi个起到作用,把每一种物品都拆成最多可能购买的数量,然后用01背包解决问题。最坏复杂度O(W* W * n)。
解决方案2
我们考虑用dp[i][j]表示已经处理了前i种物品,现在背包里的物品重量之和不大于j的情况下所能获得的最大物品价值之和。
状态转移方程:
dp[i][j]= max(dp[i-1[j], dp[i][j-wi]+vi)
dp[i-1][j]:表示不选第i种物品。
dp[i][j-wi]+vi :表示再选个第i种物品。
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
for(int j=w[i];j<=W;j++)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
//answer=dp[W]
多重背包
现在有n个物品,每个物品能取ci次,有一个背包,它所装物品重量之和不能超过W,第i个物品它的重量是wi,价值是vi, 现在的问题是,在不超过背包容量的限制下,最多能获得的物品价值之和是多少。
(1 <=n<= 2000, 0<= W<= 2000, vi>0, wi> 0)
解决方案1
把第i种物品拆成ci个,然后直接01背包求答案。
复杂度O(n * ci *W)
解决方案2
利用二进制拆分:
把最多能取ci个的物品拆分成:1,2,4,8, 16… 2^n , k 。
其中n是最大的满足1 +2 +4 +8 +16+ … + 2^n <= ci 的数。
k =ci -1 -2 -4 -8 -16 -… -2 ^n。
例子:
对于ci==7的物品,我们对于这种物品一共有8种决策,分别是选0, 1, 2, 3, 4, 5, 6, 7个。法1:拆成7个数量为1的物品(0000000-1111111)进行01背包就能得到这8种决策。 法2:如果把7拆分成数量为1个,2个,4个的三个物品(000-111),用这三个物品进行01背包也能覆盖上面这8种决策。
对于ci==12的物品,拆分成数量为1个,2个,4个,4个的物品(0000-1111),用这四个物品进行01背包(12<16)能覆盖0-12这13种决策。
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
int num;
for(num=1;num<=c[i];num*=2)
{
int cw=num*w[i];
int cv=num*v[i];
for(int j=W;j>=cw;j--)
dp[j]=max(dp[j], dp[j-cw]+cv);
c[i]-=num;
}
num=c[i];
if(num)
{
int cw=num*w[i];
int cv=num*v[i];
for(int j=W;j>=cw;j--)
dp[j]=max(dp[j],dp[j-cw]+cv);
}
}
状态压缩dp
概念
我们有多个物品,每个物品都有一个状态,我们用一个数字就去表示出所有物品的状态,这个就是状态压缩。简而言之就是把多个物品的状态压缩成一个数字。
举例
我们有4个物品,每个物品有两种状态分别是有和没有,我们用0表示没有,用1表示有。
高~>低 | 有的物品 | 没有的物品 |
---|---|---|
1010 | 1, 3 | 0, 2 |
0110 | 1, 2 | 0, 3 |
1011 | 0, 1, 3 | 2 |
对于一个压缩过后的十进制数S通过**(S>>i)& 1**获取到第i个物品的信息。 |
例如:S=10=1010 (二进制),想获取第1个物品的信息,(S>>1)=5=0101 (二进制),此时最低位就是一个物品的信息,所以(S>> 1) & 1就得到了第一个物品的信息。
简单例题
有n个城市,标号从0到n- 1,现在知道了每两个城市之间的距离,第i个城市和第j个城市之间的距离为d [i][j],现在有一个人想从0号城市开始把所有城市至少走一遍需要走多少距离。
(1 <=n<= 20, d[i][j]> 0)
解题思路
枚举所有的方案,只需要枚举1到n-1的全排列就可以,对于每一种方案计算一遍距离。时间复杂度O( (n- 1)! )
深入思考:对于所有经过城市相同,最后-一个到达城市也相同的情况,我们只需要保留距离最小的那个就可以了。
如果一共有5个城市,分别是0, 1, 2, 3, 4考虑这两种情况
0->1->3->2 | 距离=10 | (1) 这条路径不需要扩展了 |
---|---|---|
0->3->1->2 | 距离= 9 | (2) |
dp[i][mask] : 经过城市的情况是mask,最后停在i的最短距离
状态转移方程: dp[i][S] = max(dp[j][S^(1<<i)) 满足(S>>j& 1)== 1 &&j!= i
复杂度0 (n* (2 ^(n-1)))
for(int mask=0;mask<(1<<n);mask++)
for(int i=0;i<n;i++)
dp[j][mask]=inf;
dp[0][1]=0;
for(int mask=0;mask<(1<<n);mask++)
for(int i=0;i<n;i++)
if(mask>>i&1)
for(int j=0;j<n;j++)
if(j!=i&&mask>>j&1)
dp[i][mask]=min(dp[i][mask],dp[j][mask^(1<<i)]);
边栏推荐
- arm push/pop/b/bl汇编指令
- 7. How to add the Click to RecyclerView and LongClick events
- Failed to install using npx -p @storybook/cli sb init, build a dedicated storybook by hand
- 用U盘怎么重装Win7系统?如何使用u盘重装系统win7?
- Configure clangd for vscode
- 基于最小二乘法的线性回归分析方程中系数的估计
- 深入理解Golang之Map
- General syntax and usage instructions of SQL (picture and text)
- 利用plot_surface命令绘制复杂曲面入门详解
- golang之GMP调度模型
猜你喜欢
轻量化AlphaPose
FP7128内置MOS降压恒流调光深度0.01%高辉共阳调光方案
General syntax and usage instructions of SQL (picture and text)
How to reinstall Win7 system with U disk?How to reinstall win7 using u disk?
Win11 system cannot find dll file how to fix
用U盘怎么重装Win7系统?如何使用u盘重装系统win7?
FP6195耐压60V电流降压3.3V5V模块供电方案
使用 腾讯云搭建一个个人博客
关于c语言的调试技巧
Binder ServiceManager解析
随机推荐
STM32LL库使用——SPI通信
STM32LL库——USART中断接收不定长信息
jest test, component test
pygame绘制弧线
source /build/envsetup.sh和lunch)
LORA芯片ASR6505无线远距离传输8位MCU
What should I do if Windows 10 cannot connect to the printer?Solutions for not using the printer
How to update Win11 sound card driver?Win11 sound card driver update method
2021-10-14
使用libcurl将Opencv Mat的图像上传到文件服务器,基于post请求和ftp协议两种方法
Please make sure you have the correct access rights and the repository exists. Problem solved
Please make sure you have the correct access rights and the repository exists.问题解决
Article pygame drag the implementation of the method
Win10 computer can't read U disk?Don't recognize U disk how to solve?
How to add a one-key shutdown option to the right-click menu in Windows 11
5. Use RecyclerView to elegantly achieve waterfall effect
MATLAB图形加标注的基本方法入门简介
Win11没有本地用户和组怎么解决
DP4344兼容CS4344-DA转换器
FP7195降压恒流PWM转模拟调光零压差大功率驱动方案原理图