动态规划练习【一】 背包问题

详细讲解

背包问题大汇总

文章目录

    • 背包问题大汇总
    • 01背包
      • 问题:
      • 思路:
      • 空间优化复杂度
      • 代码
      • 总结:
    • 完全背包
      • 问题:
      • 思路:
      • 代码:
      • 优化
    • 多重背包
      • 问题:
      • 思路:
      • 代码:
      • 单调队列优化
    • 混合三种背包
      • 问题:
      • 思路
      • 例题
      • 代码:
    • 二维费用的背包问题
    • 分组的背包问题
    • 有依赖的背包问题
    • 背包问题的方案总数

01背包

问题:

有N件物品和一个容量为V的背包,第i件物品的费用(体积)是w[i],价值是c[i],求解将哪些物品装入背包可使这些物品的费用综合不超过背包容量,且价值总和最大

思路:

f[i][v]表示前i件物品(部分或全部)恰放入一个容量为v的背包可以获得的最大价值。
转移方程:
(f的初始值为负无穷)
f [ i ][ v ] = m a x ( f [i - 1 ] [ v ] , f [i - 1 ] [ v -w [ i ] ] + c [ i ] )
这是最基础的背包dp,我们在考虑dp问题的时候,这么想,f[i][v]的状态是怎么来的?也就是第i件物品选择方式?有两种方式,第一种就是第i件商品不拿,那么f[i][v]的状态就是前i-1件物品恰放入一个容量为v的背包(即f[i-1][v])直接转移过来的,另外一种就是第i件商品选择拿,那么f[i][v]的状态就是前i-1件物品恰放入一个容量为v-w[i]的背包(即f[i-1][v-w[i]])再加入第i件商品(c[i]),其实就是末状态(f[i][v])将第i件商品去除,可以得到初状态(f[i-1][v-w[i]])
本质就是由已知推未知
这样最后答案是max(f[N][0…V]),因为我们在定义状态f时说的“恰”,也就是最佳情况并不一定是背包要装满,所以要取各种状态的最大值。
怎么样这个“恰”去掉呢?我们可以将所有f[i][j]初始化为0,也就是最开始的f[i][j]是有意义的,只是价值为0,可以看做无物品的背包价值都是0,这样最后的结果就是f[N][V]
小结:初始化为0------>不超过容量V
初始化为负无穷,dp[i][0]=0-------->恰好为容量V

空间优化复杂度

我们可以用一维的f[i]来代替上面的二维数组
for i=1…N
for v=V…0
f [v ] = max ( f[ v ] , f[ v- w [ i ] ] + c [ i ] )
我们在每次主循环时用v=V…0的逆序推f[v],这样才能保证推f[v]时f[v-w[i]]保存的是状态f[i-1][v-w[i]]的值。
为什么倒着循环可以看看这篇文章

代码

二维:

//f[i][v]表示前i件物品,总质量不超过v的最优价值 
for(int i=1;i<=N;i++)
{for(int v=V;v>0;v--){if(w[i]<=v)f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);//如果能装下 else f[i][v]=f[i-1][v];}
}
printf("%d",f[N][V]);

一维:

//f[v]表示质量不超过v公斤的最大价值
for(int i=1;i<=N;i++)
{for(int v=V;v>=w[i];v--)//避免数组出现负下标{f[v]=max(f[v],f[v-w[i]]+c[i]);}
} 
printf("%d",f[V]);

总结:

01背包定义函数一般有dp[i]恰好花费i最佳和花费i最佳,前者是dp初始为最大值,后者初始为0
如果dp第一维不是考虑前i个商品,而是直接考虑花费这类,此时花费这类的循环要倒着进行,这样可以优化空间复杂度

完全背包

问题:

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

思路:

和01背包的区别在于,每件物品不再只有取和不取两种状态,而是有取0件,取1件,取2件…很多种。
我们用01背包的方式来做,f[i][v]表示前i种物品恰好放入一个质量为v的背包的最大权值
f [ i ] [ v ] = m a x (f [ i - 1 ] [ v - k * w [i ] ] + k * c [ i ] | 0 < =k * w [ i ] <= v )
使用一维方程:
for i=1…N
for v=0…V
f [v ] = m a x (f [ v ] , f[ v - w [i ] ] +c [ i ] )、
你会发现与01背包相比,仅仅是把v的枚举顺序倒了过来
为啥呢?
01背包中v是从V->0,这是因为为了保证第i次循环中的状态f[i][v]是由上一个状态f[i-1][v-w[i]]递推而来,这样可以确定每个物品只选一次,我们在考虑“加选第i件物品”这一策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-w[i]]
而完全背包中物品的数量是无限的,所以在考虑“加选第i件物品”时,需要的是一个已经选入第i种物品的子结果f[i][v-w[i]],也就是再已放入第i种物品的基础上再进行转移,所以要采用v=0->V的顺序
二维方程的话:
f [ i ] [ v ] = max ( f [ i - 1 ] [ v ] , f[ i ] [ v - w [ i ] ]+ c [ i ] )

代码:

f[i][v]表示前i件物品,总质量不超过v的最优价值

for(int i=1;i<=N;i++)
{for(int v=0;v<=V;v++){if(v<w[i])f[i][v]=f[i-1][v];else {f[i][v]=max(f[i-1][v],f[i][v-w[i]]+c[i]);}}}
printf("%d",f[N][V]);

f[v]表示质量不超过v公斤的最大价值

for(int i=1;i<=N;i++)
{for(int v=0;v<=V;v++){if(v>w[i])f[v]=max(f[v-w[i]]+c[i],f[v]); }
}
printf("%d",f[V]);

优化

完全背包中,如果两个物品i与j,w[i]<=w[j]&&c[i]>=c[j],物品j就可以去掉,也就是一个物品占空间又大,价值还低,那要他干啥,j完全可以被i代替。这是一个简单的小优化
我们还可以把完全背包问题转化成01背包问题来解
因为背包容量是给定的,所以就可以算出每件物品最多可以装多少,将无限数量转变成有限数量,用01背包去做
但是这样的转变也太low了,对程序优化不了多少,更高效的转化是:把第i种物品拆成费用为w[i]*2k,价值拆成c[i]*wk的若干件物品,k满足w[i]*2k<V。二进制拆分的原理我们可以用 1,2,4,8…2n 表示出1 到 2n+1−1的所有数.利用二进制的思想来优化问题,不管最优策略是选几件第i种商品,总可以写成若干个2k件物品的和。把每件物品拆成O(log(V/w[i])+1)件物品

多重背包

问题:

有N种物品和一个容量为V的背包,第i种物品最多有n[i]件可用,每件费用是w[i],价值是c[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

思路:

和完全背包很相似,区别在于每个物品的数量是明确的是固定的,那直接就可以把完全背包的式子拿过来
f [ i ] [ v ] = m a x (f [ i - 1 ] [ v - k * w [i ] ] + k * c [ i ] | 0 < =k * <= n[i] )
一维的就是 f [ v] = max ( f[ v ] , f [ v - k * w [ i] ] + k* c [ i ] );
区别是k的范围是0~n[i]
但是光能这么做怎么够,我们看看怎么转化成最基础的01背包
最简单粗暴的就是你吧第i件物品换成n[i]件01背包中物品,就是把一种多件看作是多种一件,但是这样复杂度不变O(V * sum(n[i])),意义不大,怎么才能降低负责度
完全背包我们讲了二进制的方式来优化,这里我们也考虑二进制的思想。把第i种物品换成若干件物品,每个物品有一个系数,这件物品的费用和价值等于原本物品的乘以系数,而这些系数分别是2的k次方(系数为0,2,4,),最后一个系数是n[i]-2k+1(剩下的)。例如13就可以拆分成系数为1,2,4,6(因为8达不到,所以用13减去前面的1,2,4,得到6)的四件物品
这样就将第i种物品分成O(logn[i])种物品,将原问题转化为了复杂度为O(V*sum(logn[i])的01背包问题

代码:

朴素算法:

for(int i=1;i<=n;i++)
{for(int j=V;j>=0;j--){for(int k=0;k<=n[i];k++){if(j-k*w[i]<0)break;//如果当前情况容量不足,那后面容量肯定也不够,直接跳出换成其他物品 f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);}}
}
cout<<f[V];

进行二进制优化,转化成01背包

int main()
{cin>>n>>V;for(int i=1;i<=n;i++)//把一种物品的数量按照二进制进行拆分,并乘以对应系数 {int x,y,s,t=1;cin>>x>>y>>s;//输入费用,单价值,数量 while(s>=t){n1++;w[n1]=x*t;//乘以系数 c[n1]=y*t;s-=t;t*=2;}w[++n1]=x*s;//剩余费用 c[n1]=y*s;//剩余价值 }for(int i=1;i<=n1;i++){for(int j=V;j>=w[i];j--){f[j]=max(f[j],f[j-w[i]]+c[i]);//01背包解法 }}cout<<f[V];
} 
for(int i=1;i<=n;i++)
{for(int j=1;j<=num[i];j<<=1)//二进制每一位枚举.//注意要从小到大拆分{num[i]-=j;//减去拆分出来的new_c[++tot]=j*c[i];//合成一个大的物品的体积new_w[tot]=j*w[i];//合成一个大的物品的价值}if(num[i])//判断是否会有余下的部分.//就好像我们某一件物品为13,显然拆成二进制为1,2,4.//我们余出来的部分为6,所以需要再来一份.{new_c[++tot]=num[i]*c[i];new_w[tot]=num[i]*w[i];num[i]=0;}
}

单调队列优化

多重背包最普通的状态方程:
f [ i ][ j ] = max ( f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − k ∗ c [ i ] ] + k∗ w [ i ] )

混合三种背包

问题:

如果把01背包,完全背包,多重背包混合起来。也就是说,有的物品只可以去一次(01背包),有的物品可以无限取(完全背包),有的物品可以取的次数有一个上限(多重背包),应该怎么求解呢?

思路

01背包和完全背包代码中只有v的循环顺序不同,所以可以针对不同的商品类型选用顺序或者逆序的循环即可
for i=1…N
if 第i件物品是01背包
for v = V…0
f [ v ] = max ( f [ v] , f [ v - w [ i ] ] + c [ i ] )
else if 第i件物品是完全背包
for v=0…V
f [ v ] = max ( f [ v ] , f [ v- w [ i ] ] + c [ i ] )
再加上多重背包
将多重背包分成多个01背包然后求解

例题

一个容量为m的背包,现在有n件物品,质量分别是W[i],价值分别是C[i],P[i]表示物品的数量,(0说明可以购买无数次,其他数字为购买的最多件数)

代码:

for(int i=1;i<=n;i++)
{if(完全背包) {for(int j=c[i];j<=V;j++)f[j]=max(f[j],f[j-c[i]]+w[i]);}else if(01背包) {for(int j=V;j>=c[i];j--)f[j]=max(f[j],f[j-c[i]]+w[i]); }else//否则就是多重背包了 {for(int j=V;j>=0;j--)for(int k=1;k<=num[i];k++)if(j-k*c[i]>=0)f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);}
}
int main()
{cin>>m>>n;for(int i=1;i<=n;i++)cin>>w[i]>>c[i]>>p[i];for(int i=1;i<=n;i++){if(p[i]==0)//完全背包{for(int j=w[i];j<=m;j++)f[j]=max(f[j],f[j-w[j]]+c[i]);}else {for(int j=1;j<=p[i];j++)//01背包和多重背包 {for(int k=m;k>=w[i];k--){f[k]=max(f[k],f[k-w[i]]+c[i]);}}} }cout<<f[m];
} 
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+7;
int w[maxn],v[maxn],tot[maxn];
int dp[200010];
int main()
{int V,n;scanf("%d%d",&n,&V);for(int i=1; i<=n; i++)scanf("%d%d%d",&w[i],&v[i],&tot[i]);for(int i=1; i<=n; i++){if(tot[i]==0)///完全背包for(int j=w[i]; j<=V; j++)dp[j]=max(dp[j],dp[j-w[i]]+v[i]);else///01与多重背包(二进制优化后的多重背包与01背包循环非常相似){int x=tot[i];for(int k=1; k<x; k<<=1)///此处套用二进制优化的模板{for(int j=V; j>=w[i]*k; j--)dp[j]=max(dp[j],dp[j-w[i]*k]+v[i]*k);x-=k;}if(x)for(int j=V; j>=w[i]*x; j--)///相当于还剩一个物品未考虑dp[j]=max(dp[j],dp[j-w[i]*x]+v[i]*x);}}cout<<dp[V];return 0;
}

二维费用的背包问题

分组的背包问题

有依赖的背包问题

背包问题的方案总数

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

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

相关文章

P3329-[ZJOI2011]最小割【最小割树】

正题 题目链接:https://www.luogu.com.cn/problem/P3329 题目大意 nnn个点mmm条边的无向图&#xff0c;每次询问一个xxx表示最小割不超过xxx的点对数量。 解题思路 我们对于两个点sss到ttt完成网络流后的残量网络上&#xff0c;与sss联通的点属于点集SSS&#xff0c;与ttt联通…

《.NET 性能优化》送书活动结果公布

截止到9月7日18&#xff1a;00&#xff08;规则本是12&#xff1a;00&#xff0c;忙的忘记了这事&#xff0c;18点截的图&#xff09;&#xff0c;本次送书活动《.NET 性能优化》共收到100多位同学参与回复&#xff0c;本次很多同学在看到活动的书 &#xff0c;自行就到异步社区…

【期望】期望收益(金牌导航 期望-3)

期望收益 金牌导航 期望-3 题目大意 给你一个01串&#xff0c;有些位置是未知的&#xff0c;连续的x个1贡献为想x2x^2x2&#xff0c;现在问你该串的期望贡献 输入样例 4 ????输出样例 4.1250数据范围 1⩽n⩽31051\leqslant n \leqslant 3\times 10^51⩽n⩽3105 解题…

杜教筛技巧随记

常见完全积性函数&#xff1a; ϵ(n)[n1]ϵ(n)[n1]ϵ(n)[n1]&#xff08;元函数&#xff0c;满足f∗ϵff*ϵff∗ϵf&#xff09; I(n)1I(n)1I(n)1 id(n)nid(n)nid(n)n 常见卷积&#xff1a; μ∗Iϵ\mu* Iϵμ∗Iϵ ϕ∗Iid\phi*Iidϕ∗Iid f(n)∑i1niϕ(i)f(n)\sum_{i1}^{n}i…

hdu-2844 Coins (混合背包+二进制优化)

HDU链接 文章目录题目描述&#xff1a;题意&#xff1a;题解&#xff08;代码&#xff09;题目描述&#xff1a; 输入描述: 输出描述: For each test case output the answer on a single line. 输入 3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0输出 8 4题意&#xff1a; 有n种硬币&…

CF1451F-Nullify The Matrix【结论题,博弈论】

正题 题目链接:https://www.luogu.com.cn/problem/CF1451F 题目大意 n∗mn*mn∗m的网格&#xff0c;每个网格上有数字&#xff0c;先后手轮流操作 每次操作选择一个只有往右和往下的路径&#xff0c;让第一个格子减去一个正整数并且随意修改后面的格子。要求完成后所有格子非…

.NET Core WebApi中实现多态数据绑定

什么是多态数据绑定&#xff1f;我们都知道在ASP.NET Core WebApi中数据绑定机制&#xff08;Data Binding&#xff09;负责绑定请求参数&#xff0c; 通常情况下大部分的数据绑定都能在默认的数据绑定器&#xff08;Binder&#xff09;中正常的进行&#xff0c;但是也会出现少…

【期望】期望分数(金牌导航 期望-4)

期望分数 金牌导航 期望-4 题目大意 告诉你一个01串中每个位置是1的概率&#xff0c;对于连续的x个1&#xff0c;贡献为x3x^3x3&#xff0c;问你期望贡献是多少 输入样例 3 0.5 0.5 0.5输入样例 6.0数据范围 1⩽N⩽1051\leqslant N \leqslant 10^51⩽N⩽105 解题思路…

[51NOD1847]奇怪的数学题(杜教筛+min_25筛+第二类斯特林数)

f(x)f(x)f(x)表示xxx的次大约数&#xff0c;有f(x)xx的最小质因数f(x)\frac{x}{x的最小质因数}f(x)x的最小质因数x​&#xff0c;那么 ∑i1n∑j1nsgcd(i,j)k∑i1n∑j1nf(gcd(i,j))k∑d1nf(d)k∑i1n∑j1n[gcd(i,j)d]∑d1nf(d)k∑i1⌊nd⌋∑j1⌊nd⌋[gcd(i,j)1]∑d1nf(d)k⋅(2∑i…

hdu 1059 Dividing

Hdu链接 文章目录题目描述题意&#xff1a;题解&#xff1a;代码&#xff1a;题目描述 输入描述: 输出描述: 示例1 输入 1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0输出 Collection #1: Cant be divided.Collection #2: Can be divided.题意&#xff1a; 有价值分别是1~6的6种…

UOJ#284-快乐游戏鸡【长链剖分,线段树】

正题 题目链接:https://uoj.ac/problem/284 题目大意 nnn个点的一棵树&#xff0c;每个点有一个wiw_iwi​表示至少死亡wiw_iwi​次才能通过这个点&#xff0c;否则就会死亡。只能往子节点走&#xff0c;mmm此询问从sis_isi​走到tit_iti​至少要死多少次。 解题思路 也就算我…

【期望】彩色圆环(金牌导航 期望-5)

彩色圆环 金牌导航 期望-5 题目大意 给你一个环&#xff0c;每个位置的数字等概率为1~m中的其中一个&#xff0c;对于连续的相同数字的串&#xff0c;记其长度为aia_iai​&#xff0c;求aia_iai​的积的期望值 输入样例 8 1输出样例 8.00000数据范围 1⩽N⩽200,1⩽M⩽109…

概率与期望技巧随记

1.已知某事件发生的概率为ppp&#xff0c;则要让该事件发生所需的试验次数期望值为1p\frac{1}{p}p1​ 证明&#xff1a;Exp1(1−p)(Ex1)E_xp\times 1(1-p)\times(E_x1)Ex​p1(1−p)(Ex​1) 易解得Ex1pE_x\frac{1}{p}Ex​p1​

P2597-[ZJOI2012]灾难【DAG支配树】

正题 题目链接:https://www.luogu.com.cn/problem/P2597 题目大意 nnn个点的一张DAGDAGDAG&#xff0c;对于每个点xxx求有多少点yyy满足从yyy出发到达某个出度为000的所有路径都必须经过xxx。 解题思路 首先建立一张反图&#xff0c;然后一个超级源点连向所有入度为000的节点…

hdu-1171 Big Event in HDU

hdu试题链接 文章目录Problem Description题意&#xff1a;题解&#xff1a;代码&#xff1a;Problem Description Nowadays, we all know that Computer College is the biggest department in HDU. But, maybe you don’t know that Computer College had ever been split in…

.Net Core应用框架Util介绍(二)

Util的开源地址https://github.com/dotnetcore/utilUtil的开源协议Util以MIT协议开源&#xff0c;这是目前最宽松的开源协议&#xff0c;你不仅可以用于商业项目&#xff0c;还能把Util的代码放进你的框架&#xff0c;放心使用。Util的命名Util这个名字看上去不怎么高大上&…

【期望】【高斯消元】图上游走(金牌导航 期望-6)

图上游走 金牌导航 期望-6 题目大意 给出一个无向连通图&#xff0c;小明初始在点1&#xff0c;每一步等概率地走向相连的其他点&#xff0c;当走到n时结束&#xff0c;定义分数从1为走到n的过程中经过的边的编号之和&#xff0c;现在让你给这m条边重新编号&#xff0c;使最…

[CSA35G][XSY3318]Counting Quests (DP)

XSY3318 CSA35G 对于一个询问区间的集合SSS&#xff0c;求出每一个数被哪些区间覆盖了&#xff0c;记为SiS_iSi​。 要能保证猜出选中数&#xff0c;当且仅当每个数的SiS_iSi​互不相同。 考虑求出不满足要求的集合SSS的个数。 首先可以观察得到SiS_iSi​的一个性质&#xff1…

hdu-1114 Piggy-Bank

文章目录Problem Description题意&#xff1a;题解&#xff1a;代码&#xff1a;hdu-1114Problem Description Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversib…

ATcoder-[AGC048B]Bracket Score【结论,贪心】

正题 题目链接:https://atcoder.jp/contests/agc048/tasks/agc048_b 题目大意 长度为nnn的合法括号序列可以包含[...][...][...]和(...)(...)(...)。 如果在第iii个位置是′(′\ (\ ′ ( ′ 或者 ′)′\ )\ ′ ) ′那么可以获得aia_iai​的权值&#xff0c;否则获得bib_ibi​…