当前位置:网站首页>背包问题-动态规划-理论篇
背包问题-动态规划-理论篇
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)]);
边栏推荐
- 总结计算机网络超全面试题
- jest test, component test
- FP7126降压恒流65536级高辉无频闪调光共阳极舞台灯RGB驱动方案
- Please make sure you have the correct access rights and the repository exists.问题解决
- How to set the win10 taskbar does not merge icons
- GICv3/v4-软件概述
- Daily - Notes
- FP6293电池升压5V-12V大电流2APWM模式升压方案
- vscode镜像
- CI24R1小模块2.4G收发模块无线通信低成本兼容si24r1/XN297超低功耗
猜你喜欢
为android系统添加产品的过程
使用 腾讯云搭建一个个人博客
What should I do if Windows 10 cannot connect to the printer?Solutions for not using the printer
Win10 Settings screen out from lack of sleep?Win10 set the method that never sleep
Win10 cannot directly use photo viewer to open the picture
win10怎么设置不睡眠熄屏?win10设置永不睡眠的方法
win11一直弹出用户账户控制怎么解决
Mysql connection error solution
小T成长记-网络篇-1-什么是网络?
FP7128内置MOS降压恒流调光深度0.01%高辉共阳调光方案
随机推荐
Mysql connection error solution
Fast advanced TypeScript
基于最小二乘法的线性回归分析方程中系数的估计
LORA芯片ASR6601支持M4内核的远距离传输芯片
Win11声卡驱动如何更新?Win11声卡驱动更新方法
Win11没有本地用户和组怎么解决
日常-笔记
FP7195芯片PWM转模拟调光至0.1%低亮度时恒流一致性的控制原理
【我的电赛日记(三)】STM32学习笔记与要点总结
Spark及相关生态组件安装配置——快速回忆
Win10电脑需要安装杀毒软件吗?
Win7 encounters an error and cannot boot into the desktop normally, how to solve it?
FP7195转模拟调光技术解决智能家居调光频闪和电感噪音的原理
基于矩阵计算的线性回归分析方程中系数的估计
镜像法求解接地导体空腔电势分布问题
How to add a one-key shutdown option to the right-click menu in Windows 11
【使用Pytorch实现VGG16网络模型】
FP6293电池升压5V-12V大电流2APWM模式升压方案
FP7126降压恒流65536级高辉无频闪调光共阳极舞台灯RGB驱动方案
arm ldr系列指令