目录

一、概述

二、分类

三、01背包

01背包模板

采药

开心的金明

小A点菜

装箱问题

榨取kkksc03

严酷的训练

L国的战斗之间谍

音量调节

精卫填海

四、完全背包

完全背包模板

疯狂的采药

五、分组背包

分组背包模板

通天分组之背包

六、多重背包

多重背包模板

mc生存

七、不是很正规严谨的总结

八、转载与补充


一、概述

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

也可以将背包问题描述为决定性问题,即:在总重量不超过W的前提下,总价值是否能达到V?

二、分类

  • 0-1背包:最基本的背包问题,每个物品最多只能放一次。
  • 完全背包:每种物品可以放无限多次。
  • 多重背包:每种物品有一个固定的次数上限。
  • 混合三种背包:将前三种的问题叠加成较复杂的问题。
  • 二维费用的背包:费用cost增加了一维。
  • 分组背包:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价。
  • 有依赖背包:给物品的选取加上限制的方法。
  • 泛化物品的背包问题:没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化。
  • 背包问题问法的变化:除在背包容量的限制下求可取最大价值外的其他问法。

三、01背包

概念:每种物品仅有一件,可以选择放或不放。

问题:有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是c[i]。求解将哪些物品装入背包可使价值总和最大

分析:

子问题:将前i件物品放入容量为v的背包中。

若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。

  1. 如果不放第i件物品,那么子问题就转化为“前i-1件物品放入容量为v的背包中”,价值为:f[i-1][v]
  2. 如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-w[i]的背包中”,此时能获得的最大价值就是f[i-1][v-w[i]]再加上通过放入第i件物品获得的价值w[i],即:f[i-1][v-w[i]]+c[i]

f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。

状态转移方程:f[i][v]=max(f[i-1][v] , f[i-1][v-w[i]]+c[i] )

【初始化的细节问题】

有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满,而是只希望价格尽量大。这两种问法的区别是初始化方式不同

  1. 如果要求恰好装满背包,那么在初始化时除了f[0]为0其它f均设为-∞
  2. 如果要求价格尽量最大,初始化时应该将f[0..V]全部设为0

原因:初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。

  1. 如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。
  2. 如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

【优化空间复杂度】

以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。

第一个优化:我们发现,每一种状态只和i-1有关,所以干脆把i这个维度去掉,j递减从后往前更新一个一维数组(后边更新完的d[j]代表d[i][j],前边的旧值代表d[i-1][j],刚好后边用前边的,前边还不会被覆盖)

第二个优化:v不用减到0,只需要减到w[i]就行了

01背包模板

for(int i=1;i<=n;i++)for(int j=v;j>=w[i];j--)f[j]=max(f[j],f[j-w[i]]+c[i]);

在此更好地理解一下为啥用动态规划解决背包问题而不是用贪心: 

如果用贪心的话,在某个时候,我们无法知道背包哪种状态才是最优的,因此,记录下所有状态能够达到的最优的情况,最后在这些最优的情况当中选取一个最优的——动态规划求的是所有状态的最优解。

在背包问题中,当前背包容量的使用状况,就是状态。

一个物体,放不放进背包?这就是决策。

放进背包后,背包容量发生了变化,这就是状态的转移。

决策会带来状态的转移,不同的决策会带来不同的转移。

思考:

假如我们已经知道了背包体积是3时的最大价值是5,这时候我们决定放入一个体积是4,价值是5的物品,那么背包的体积会增加到7,那么,这个时候获得的是体积7的最优解吗?

很有可能不是~~~最简单的例子,假如我们有一个体积是7,价值是20的物品。那么显然要比放这两个物品更优。因此,

最优的状态转移出去,并不一定也能得到其他状态的最优值。

——局部最优没有传递性

但是!我们把问题反过来就不一样了!

如果我们知道了体积6的最优解,并且还知道它是由体积等于4转移得到的,那么我们能不能确定体积4的状态也是对应的最优解?

这次的答案就变了,是正确的,因为如果体积4时还有更好的解法,那么体积6理应也会变得更好才对,这和我们的假设矛盾了。

局部最优一定是由其他局部最优的状态转移得到的

而且状态的转移也是有顺序的——随着放物品,背包容量只会由大变小

我们并不知道当前的转移能否达到最优状态,所以我们需要用一个数组来记录所有状态历史上曾经达到过的最值。最后从所有的最值当中再选出一个最值来,就是最后问题的解。

理解一下遍历顺序为啥从小的开始

从最初状态开始,就是背包是空的时候,这时候的价值是0,体积也是0,也是它的最优状态

从0开始转移状态,状态转移伴随着决策,在这题当中体现在选取不同的物品上。我们遍历物品,作为决策,再遍历能够应用这些决策的状态,就拿到了所有的状态转移。最后,我们用一个容器记录一下所有状态转移过程当中达到的局部最优解

采药

[NOIP2005 普及组] 采药 - 洛谷https://www.luogu.com.cn/problem/P1048

完全套模板的01背包

#include<iostream>
using namespace std;int T;  //总共能采药的时间(相当于背包总容量)
int M;  //草药总数目(相当于物品总数量)
int w[105]; //采每一种草药花的时间(相当于每件物品的重量)
int v[105]; //每种草药的价值(相当于每件物品的价值)
//标准的01背包——每种物品要么取,要么不取,在不超过T的情况下获得最大价值
int dp[1005];   //dp[i]代表用时i所能获取的最大价值 int main(){cin>>T>>M;for(int i=1;i<=M;i++)cin>>w[i]>>v[i];for(int i=1;i<=M;i++){for(int j=T;j>=w[i];j--){dp[j]=max(dp[j-w[i]]+v[i],dp[j]);}}cout<<dp[T];
}

开心的金明

开心的金明https://www.luogu.com.cn/problem/P1060

搞清题意就可以完全套模板

#include<iostream>
using namespace std;int N;  //总钱数 (背包总容量)
int M;  // 想买物品数 (物品总数)
int v[30];  //每种物品的价格(每种物品的重量)
int p;      //重要度(本题特有的)
//发现没有熟悉的"物品价值”?
//原来v*p就是“物品价值”~设个val数组
int val[30];
int dp[30001];int main(){cin>>N>>M;for(int i=1;i<=M;i++){cin>>v[i]>>p;val[i]=v[i]*p;}for(int i=1;i<=M;i++){for(int j=N;j>=v[i];j--){dp[j]=max(dp[j-v[i]]+val[i],dp[j]);}}cout<<dp[N];
}

小A点菜

P1164 小A点菜 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1164xhttps://www.luogu.com.cn/problem/P1164

dp[i][j]代表前i道菜中选恰好花了j元(类比一下摆花问题中前n种恰好摆出m盆~!啊,都是一样的套路!)

#include<iostream>
using namespace std;int M;
int N;
int a[1005];
int dp[105][10005];
//dp[i][j]是从前i种菜中选,刚好花j元的种类数**
//因为我们要求的是dp[N][M]呀 int main(){cin>>N>>M;for(int i=1;i<=N;i++)   //输入价格 cin>>a[i];dp[0][0]=0;for(int i=1;i<=N;i++){for(int j=0;j<=M;j++){if(j<a[i])dp[i][j]=dp[i-1][j];if(a[i]==j)dp[i][j]=dp[i-1][j]+1;if(j>a[i])dp[i][j]=dp[i-1][j]+dp[i-1][j-a[i]]; }} cout<<dp[N][M];
}

进行一下优化,去一个维度,就能变成模板形式(递推式多多理解,这个不是求最大值,而是求所有方案数加起来):

#include<iostream>
using namespace std;
int main()
{int money,num;int price[1000],plan[10000]={0};int i,j;cin>>num>>money;for(i=1;i<=num;i++)        cin>>price[i];//输入菜品价格plan[0]=1;//至少有一种方案for(i=1;i<=num;i++)//每种菜依次进行比较{for(j=money;j>=price[i];j--)//从现有钱数开始,直至当前菜为止{plan[j]=plan[j]+plan[j-price[i]];//当前的花费=之前的花费+不点这个菜的花费}}cout<<plan[money]<<endl;//输出最后一次的花费return 0;
}

装箱问题

(58条消息) 装箱问题(洛谷-P1049)_Alex_McAvoy的博客-CSDN博客https://blog.csdn.net/u011815404/article/details/81808205

直接套模板

#include<iostream>
using namespace std;int V;  //箱子容量
int n;  //物品数量
int w[35];//体积
//任选若干个装入箱子内使箱子剩余空间最小
int dp[20005];int main(){cin>>V>>n;for(int i=1;i<=n;i++){cin>>w[i];}for(int i=1;i<=n;i++)for(int j=V;j>=w[i];j--){dp[j]=max(dp[j],dp[j-w[i]]+w[i]);}cout<<V-dp[V];}

榨取kkksc03

P1855 榨取kkksc03 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

每个愿望又花时间又花金钱,显然这是一个二维花费的背包问题

其实很简单,费用多了一维,让数组也多一维就ok了,下面的模板是优化后的~~~~

二维花费背包的模板:

for (int i=1; i<=N; i++)for (int j=V; j>=v[i]; j--)for (int k=U; k>=u[i]; k--) s[j][k]=max(s[j][k], s[j-v[i]][k-u[i]]+c[i]);

套二维01背包的模板

#include<iostream>
using namespace std;int n;  //n个愿望
int M;  //M元
int T;  //时间
int m[105]; //第i个愿望所需的金钱
int t[105]; //第i个愿望所需的时间
int dp[205][205];
//求最多可以实现愿望的个数
int main(){cin>>n>>M>>T;for(int i=1;i<=n;i++)cin>>m[i]>>t[i];for(int i=1;i<=n;i++)for(int j=M;j>=m[i];j--)for(int k=T;k>=t[i];k--)dp[j][k]=max(dp[j][k],dp[j-m[i]][k-t[i]]+1);cout<<dp[M][T];
}

P1507 NASA的食物计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

完全是二维代价的01背包模板

#include<iostream>
using namespace std;int V,M;
int N;
int m[55],v[55],calo[55];
int dp[405][405];int main(){cin>>V>>M;cin>>N;for(int i=1;i<=N;i++)cin>>v[i]>>m[i]>>calo[i];for(int i=1;i<=N;i++)for(int j=V;j>=v[i];j--)for(int k=M;k>=m[i];k--)dp[j][k]=max(dp[j][k],dp[j-v[i]][k-m[i]]+calo[i]);cout<<dp[V][M];
}

严酷的训练

严酷的训练https://www.luogu.com.cn/problem/P2430

绕弯的点并不在背包上,还是套模板。。

#include<iostream>
using namespace std;int SL,TL;  //学生和老王的水平值
int m,n;    //题目的总数和知识点总数
int tim[5010];  //老王做题时间--->学生做题时间就是tim[i]*(TL/SL)
int p[5010],q[5010];//题目所属知识点和对应的奖励值
int T;      //规定的时间
//在T时间内,做每道题的时间是tim[p[i]],奖励值是q[i]
int dp[5005];
int main(){cin>>SL>>TL;cin>>m>>n;for(int i=1;i<=n;i++){cin>>tim[i];tim[i]*=(TL/SL); //第i个知识点花费时间} for(int i=1;i<=m;i++)cin>>p[i]>>q[i];    //所属知识点和奖励值 cin>>T;for(int i=1;i<=m;i++){for(int j=T;j>=tim[p[i]];j--){dp[j]=max(dp[j],dp[j-tim[p[i]]]+q[i]);}}cout<<dp[T];
}

L国的战斗之间谍

L国的战斗之间谍https://www.luogu.com.cn/problem/P1910

二维花费01背包 ,伪装能力和工资是两个花费,然后又是。。模板,最后找个最大值就行

#include<iostream>
using namespace std;int N;      //人数 (物品数)
int M;      //探查间谍
int X;      //钱(背包容量)
int A[1005];    //得到的资料数(物品价值)
int B[1005];    //伪装能力
int C[1005];    //工资(物品重量)
int f[1005][1005];  //j工资去雇人的价值
int ans;//求能拿到的最大资料(最大价值) int main(){cin>>N>>M>>X;for(int i=1;i<=N;i++)cin>>A[i]>>B[i]>>C[i];for(int i=1;i<=N;i++)for(int j=M;j>=B[i];j--)for(int k=X;k>=C[i];k--)f[j][k]=max(f[j][k],f[j-B[i]][k-C[i]]+A[i]);cout<<f[X][M];return 0;
}

音量调节

音量调节https://www.luogu.com.cn/problem/P1877

题意尚需更好的理解...

#include<iostream>
using namespace std;
int c[10010];   //每首歌前音量的改变量,可+c[i]可-c[i]
bool p[100][1050];//dp[i][j]代表对于第i次操作后,音量为j是否可行
int main()
{int n,bel,mal;cin>>n>>bel>>mal;for(int i=1;i<=n;i++)cin>>c[i];p[0][bel]=1;//没有演出的时候开始音量一定是可以达到的for(int i=1;i<=n;i++){for(int j=0;j<=mal;j++){if(p[i-1][j]&&j+c[i]<=mal)p[i][j+c[i]]=true;//原来的音量可行且能在这个基础上调大 if(p[i-1][j]&&j-c[i]>=0)p[i][j-c[i]]=true;//原来的音量可行且能在这个基础上调小 }}for(int i=mal;i>=0;i--)//音量从大到小,找到最大的可行音量 {if(p[n][i]==1){//找到最大的可达到音量输出之后直接结束cout<<i<<endl;return 0;}}   cout<<"-1"<<endl;return 0;
} 

精卫填海

精卫填海https://www.luogu.com.cn/problem/P1510

除了一些限制条件还是模板

#include<iostream>
using namespace std;int v;      //要v才能填平
int n;      //剩的石头数
int c;      //体力
int k[10000+5];    //每块的体积
int m[10000+5]; //每块用的体力
int dp[10000+5];//算出最大价值和v比一下
//同时要输出还剩最大体力 int main(){cin>>v>>n>>c;for(int i=1;i<=n;i++)cin>>k[i]>>m[i];for(int i=1;i<=n;i++){for(int j=c;j>=m[i];j--){dp[j]=max(dp[j],dp[j-m[i]]+k[i]);}}if(dp[c]<v)cout<<"Impossible";
//如果最终能填完,还要输出最大体力
//那就先找到差一点没填完的else{int i=c;while(dp[i]>=v)i--;cout<<c-(i+1);}
}

~~~~~~~~~~~~~~~~~~比较难的题分界线~~~~~~~~~~~~~~~~~~

最大约数和

P1734 最大约数和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

这题终于不是套模板了

分析题干可知:要选出一些数,这些数有它们的贡献,每个数有自己的大小这个代价,要求这些数加起来不超过S

那么——这些数就是备选物品,大小就是重量,约数和就是贡献(价值),代价就是重量,S就是我们滴背包!

于是又变成了01背包的模板!()

要提前进行处理:要获得一个“物品价值”的数组,先把1~S的约数和都算出来存好

#include<iostream>
#include<math.h>
using namespace std;int S;
int a[1005];
int f[1005]; //f[j]是优化后的二维数组,j是当前不超过j的数(类似背包问题中背包容量为j时) //函数功能:求一个数所有的约数之和
int Sum(int n){int sum=0;for(int i=1;i<n;i++){if(n%i==0){sum+=i;}}return sum;
} int main(){cin>>S;for(int i=1;i<=S;i++){a[i]=Sum(i);}for(int i=1;i<=S;i++)for(int j=S;j>=i;j--)f[j]=max(f[j],f[j-i]+a[i]);  //01背包(一维数组优化)cout<<f[S];
}

P2663 越越的组队 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

❤这道题适合多看❤

它不同于这篇文章里记录的其它dp数组,其它数组直接记录能达到的最大分数,这个dp数组记录的是bool值,也就是,能不能达到这个值。然后dp[j][k]就代表选j个人能否达到k分,那么我们从一半的分数开始减,遇到第一个dp[一半人数][i]==true时,i就是选一半人能达到的最大分数!

一开始被难到的点:从所有同学中选出一半

这个一半很难控制..

将 dp[i][j] 表示为前 i 个人是否得到 j 分

but这样没有保证选择 一半!!

缺个状态没法表示咋办------>升维记录状态

dp[i][j][k]代表从i个人中选j个是否能凑出k分

再压成二维数组,就可以得到正确的转移方程~~~

(还要注意,这个状态转移方程因为是bool类型的dp,所以进行或运算,有一个能成立那么值就是true)

#include<iostream>
using namespace std;int a[105]; //分数
bool dp[105][10005];
int n;int main(){cin>>n;int sum=0;for(int i=1;i<=n;i++){cin>>a[i];sum+=a[i];  //要记录总成绩 }dp[0][0]=true;   //初始化for(int i=1;i<=n;i++){for(int j=i;j>=1;j--){for(int k=sum;k>=a[i];k--){//这个就套01背包模板dp[j][k]|=dp[j-1][k-a[i]]; }}} sum/=2;n/=2;for(int i=sum;i>=0;i--){ //这样就能保证输出的i是最大值if(dp[n][i]){    //这样就能保证,选一半人遇到能达到的最大分数时立刻输出!cout<<i<<endl; break;}}
}

四、完全背包

【题目】:

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

【思路】:

这个问题非常类似于01背包问题,所不同的是每种物品有无限件。从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……等很多种。

如果仍然按照解01背包时的思路,令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程,这件物品放k个,像这样:f[i][v]=max( f[i-1][v-k*w[i]] + k*c[i] )  (0<=k*w[i]<=v)

这跟01背包问题一样有O(N*V)个状态需要求解,但求解每个状态的时间已经不是常数了,求解状态f[i][v]的时间是O(v/w[i]),总的复杂度是超过O(V*N)的。

将01背包问题的基本思路加以改进,得到了这样一个清晰的方法。这说明01背包问题的方程的确是很重要,可以推及其它类型的背包问题。但我们还是试图改进这个复杂度。

不妨去看一下01背包优化成一维数组之后的解法,这个解法的重点是j要从V逆序递减——如果j从0~V会导致出什么样的错呢?因为后边的值要用到前面的值,结果现在从前边更新,就把原来的值覆盖掉了,这时候更新到后边,逻辑上就成了,在已经取过这件物品后又取了一次,然而,这不就恰好是完全背包的要求吗!

优化思路:

在模板上,完全背包就是把j行从0~V(把01背包的遍历顺序倒过来)

完全背包就是无限制的01背包!

完全背包模板

for(int i=1;i<=m;i++){for(int j=0;j<=v;j++)if(j>=w[i])dp[j]=max(dp[j],dp[j-w[i]]+c[i]);

疯狂的采药

疯狂的采药https://www.luogu.com.cn/problem/P1616

#include<iostream>
using namespace std;int t;      //给的采药总时间
int m;      //草药的数量
int a[10000000+5]; //采集该草药花费的时间
long long b[10000000+5]; //草药价值
long long dp[10000000+5];int main(){cin>>t>>m;for(int i=1;i<=m;i++)cin>>a[i]>>b[i];for(int i=1;i<=m;i++){for(int j=0;j<=t;j++)if(j>=a[i])dp[j]=max(dp[j],dp[j-a[i]]+b[i]);}cout<<dp[t];
}

pP7774 [COCI 2009-2010 #2] KUTEVI - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

看哪个b[i]能被a[i]凑出来

多看看

dp阶段:

dp[i]代表i是否能被a数组里的数凑出来

第一层循环:i从1遍历到n(对每个数a[i]做出取或不取的决策)

第二层循环:j从0~1000(这样就能涵盖所有b[ ])

dp[j]可能是之前的结果+a[i](这要保证j>=a[i])而来,也可能是-a[i]而来

现在我们把答案都存到dp[]数组啦!之后1~m遍历一遍即可

#include<iostream>
using namespace std;int n,m,a[15],b[15],f[1005];//f[i]代表i能否被a中几个数的和表示出来 int main(){cin>>n>>m;for(int i=1;i<=n;i++)cin>>a[i];for(int i=1;i<=m;i++)cin>>b[i];f[0]=1;   //初始化 for(int i=1;i<=n;i++)  //i--->a[i],是对要不要a[i]做决策for(int j=0;j<=1000;j++){ if(j>=a[i])f[j%360]|=f[(j-a[i])%360];f[j%360]|=f[(j+a[i])%360];} for(int i=1;i<=m;i++){if(f[b[i]])cout<<"YES"<<endl;else cout<<"NO"<<endl; }return 0;
} 

P1832 A+B Problem(再升级) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

给定一个正整数n,求将其分解成若干个素数之和的方案总数

这道题也不套路,缺乏学习,应该多看看!

这道题的dp过程是:

dp[i]代表i被素数之和表示的方案数

第一层循环:i遍历n前所有数(对每个数做出取或不取的决策)

第二层循环:j从i到n(也就是看看哪个j能被i及i之前的数表示)

dp[j]+=dp[j-i]

#include<iostream>
using namespace std;//给定一个正整数n,求将其分解成若干个素数之和的方案总数
long long dp[1001]; //dp[i]代表第i个数的组成总数
bool b[1001];   // b[i]代表i是否是素数//用筛法判断是否为素数(待学习...)
void IsP(){for(int i=2;i<=500;i++)if(!b[i])for(int j=2;i*j<=1000;j++)b[i*j]=1;
} int main(){int n;cin>>n;IsP();  //预处理,筛出素数dp[0]=1; //初始化for(int i=2;i<=n;i++){ //考虑每个数取不取 if(!b[i]){//是素数才能考虑取不取for(int j=i;j<=n;j++) //从i到n是因为,要得到的数肯定不小于取的数 dp[j]+=dp[j-i]; //dp[j-i]是取这个数的情况,累加到总方案数(就像背包的dp[j-w[i]])) } } cout<<dp[n];
}

关于动归背包的循环顺序

通过上面两道“非常规套路”,发现了新的套路

在写dp时总会疑惑循环的顺序,我们发现,这两道题还有模板题都是这样的:

第一层循环内容是待选物品(背包问题的待选物品/去求和等于要求的数的备选数/)

第二层循环的内容是限制条件(背包容量/要组成的数)

第三层循环的内容是其它的一些要求(分组时小组内循环/多重背包时选择本件物品的数量)

五、分组背包

【题意】

有N件物品和一个容量为V的背包。第i件物品的体积w[i],价值是c[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。

求:将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

【分析】

这个问题变成了每组物品有若干种策略:是选择本组的某一件,还是一件都不选。

优化空间后的代码应为:

设f[k][v]表示前k组物品花费费用v能取得的最大权值,则有:f[k][v]=max(f[k-1][v],f[k-1][v-w[i]]+c[i] | 物品i属于第k组)

分组背包模板

for(int i=1;i<=k;i++)for(int j=V;j>=0;j--)    for(int b=0;b<=nums[i];b++)   //b是第i小组中的元素如果能放下这件f[v]=max{f[v],f[v-w[b]]+c[b]}

注意这里的三层循环的顺序,“for v=V..0”这一层循环必须在遍历该小组所有物品之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。

通天分组之背包

通天之分组背包

这道题和【严酷的训练】似的,不要被数组弄晕了,要弄清要记录什么量,啥时候更新这个量,什么数组表示什么,还有就是多多理解dp时的遍历顺序,以及优化

别的是只和i与i-1间有关,这个是只和i和i-1组有关,所以都是去掉这一维来优化!

还有就是这道题没给背包容量,其实这个限制和背包容量是一样的,都是一个“最大重量”的限制,都一样

#include<iostream>
using namespace std;int m,n;//n件物品总重m
int w[1005];    //重量
int v[1005];    //价值
int x[1005];    //每个小组的物品数量
int g[1005][1005];  //g[i][b]代表第i小组的第b个输入序号是几
int dp[1005];
int num;    //最大组数 int main(){cin>>m>>n;int index;for(int i=1;i<=n;i++){cin>>w[i]>>v[i]>>index;x[index]++;num=max(num,index); g[index][x[index]]=i;} //遍历所有小组 for(int i=1;i<=num;i++)//遍历所有背包容量(在这就是物品总重)for(int j=m;j>=0;j--)//遍历这一组的每一件物品,要么放某一件,要么不放for(int b=0;b<=x[i];b++){if(j>=w[g[i][b]])dp[j]=max(dp[j],dp[j-w[g[i][b]]]+v[g[i][b]]);} cout<<dp[m];
}

六、多重背包

【题意】

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

【基本思路】

和完全背包问题很类似,基本的方程只需将完全背包问题的方程略微一改即可。

对于第 i 种物品有这几种策略:取 0 件,取 1 件……取 num[i] 件,令 f[i][v] 表示前 i 种物品恰放入一个容量为 V 的背包的最大权值。

其与完全背包的区别在于,完全背包中的物品是不限量的,而多重背包的第 i 种物品最多取 num[i] 个

在记忆上,多重背包比完全背包多了一层k循环表示选几个,和分组背包相比,分组背包的第三层循环表达的是选这组中的第几个,多重背包第三层循环表达的是这件物品选几个

多重背包模板

for(i=1;i<=N;i++)//N种物品for(j=V;j>=0;j--)//容量为Vfor(k=0;k<=num[i];k++)//每种物品最多有num[i]个if(j-k*w[i]>=0)//当前容量-k个物品的重量>=0f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);

mc生存

mc生存https://www.luogu.com.cn/problem/P1964

#include<iostream>
#include<string>
using namespace std;
//背包有21格
int m;  //一开始背包里的东西
int n;  //卖n种物品
int a[105]; //每个物品的数量
int b[105]; //每个物品的价值
int c[105]; //1格可以放多少个
string st[105]; //名字 //也就是现在背包还剩21-m格,要在这个基础上把最大价值的物品组合放进背包 int dp[25]; //dp[j]代表此时放入容量为j个格子的背包能获得的最大价值(i维度优化了)
//格数问题在这解决!!
//有a个,每格最多放x个,返回值是它占多少格儿
int upint(int a,int x){int r=x;while(1){//能放下a个时,返回r/x if(r>=a) return r/x;//还放不下,就扩大一倍 r+=x;}
}int main(){cin>>m>>n;int cnt=0;//记录物品数量for(int i=1;i<=n;i++){cin>>a[i]>>b[i]>>c[i]>>st[i];int flag=0;//记录和前边的某种是不是一种物品for(int j=1;j<=i-1;j++)if(st[j]==st[i]){a[j]+=a[i];flag=1;break;}if(flag==0)cnt++;    //如果是一种新物品,cnt++}m=21-m; //当前背包所剩格子数 //同样要注意遍历顺序//每种物品 for(int i=1;i<=cnt;i++){//背包格子数 for(int j=m;j>=0;j--){//这种物品的件数 for(int k=0;k<=a[i];k++){int w=upint(k,c[i]);if(j>=w)dp[j]=max(dp[j],dp[j-w]+k*b[i]);//乘上这个k,和分组背包只能选一件就不一样啦}} }    cout<<dp[m];}

七、不是很正规严谨的总结

八、转载与补充

大佬的模板

【**背包dp**】相关推荐

  1. 【LibreOJ】#6395. 「THUPC2018」城市地铁规划 / City 背包DP+Prufer序

    [题目]#6395. 「THUPC2018」城市地铁规划 / City [题意]给定n个点要求构造一棵树,每个点的价值是一个关于点度的k次多项式,系数均为给定的\(a_0,...a_k\),求最大价值 ...

  2. 背包dp的核心思想(动态规划)

    背包dp 突然发现我一直没有真正理解背包,真正的背包应该是用空间换时间的一种dp方法,本质上就是n个物品选或不选,理论上有2n2^n2n种可能结果,但是背包利用的就是值域很小这一特点来重叠子结构,所以 ...

  3. Codeforces 864E Fire(背包DP)

    背包DP,决策的时候记一下 jc[i][j]=1 表示第i个物品容量为j的时候要选,输出方案的时候倒推就好了 #include<iostream> #include<cstdlib& ...

  4. 【bzoj4007】[JLOI2015]战争调度 暴力+树形背包dp

    题目描述 给你一棵 $n$ 层的完全二叉树,每个节点可以染黑白两种颜色.对于每个叶子节点及其某个祖先节点,如果它们均为黑色则有一个贡献值,如果均为白色则有另一个贡献值.要求黑色的叶子节点数目不超过 $ ...

  5. HDU 1011 Starship Troopers 树形+背包dp

    http://acm.hdu.edu.cn/showproblem.php?pid=1011   题意:每个节点有两个值bug和brain,当清扫该节点的所有bug时就得到brain值,只有当父节点被 ...

  6. 【bzoj1222】[HNOI2001]产品加工 背包dp

    题目描述 某加工厂有A.B两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成.由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工 ...

  7. 【bzoj4753】[Jsoi2016]最佳团体 分数规划+树形背包dp

    题目描述 JSOI信息学代表队一共有N名候选人,这些候选人从1到N编号.方便起见,JYY的编号是0号.每个候选人都由一位编号比他小的候选人Ri推荐.如果Ri=0则说明这个候选人是JYY自己看上的.为了 ...

  8. 【bzoj5072】[Lydsy十月月赛]小A的树 树形背包dp

    题目描述 给出一棵n个点的树,每个点有黑白两种颜色.q次询问,每次询问给出x和y,问能否选出一个x个点的联通子图,使得其中黑点数目为y. 输入 第一行一个正整数 T 表示数据组数. 对于每一组数据,第 ...

  9. 【背包DP练习】洛谷 P5020货币系统 P1757通天之分组背包 P1064[NOIP2006 提高组]金明的预算方案 P5322 [BJOI2019]排兵布阵

    洛谷 P5020货币系统 https://www.luogu.com.cn/problem/P5020 思路是把货币从小到大排序,然后按顺序依次完全背包dp,每次dp检查i-1种面值的货币能不能凑出第 ...

  10. hdu 5234 Happy birthday 背包 dp

    Happy birthday Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?p ...

最新文章

  1. PCL点云特征描述与提取(2)
  2. 综述 | 现在是蛋白质组学数据共享和再分析的黄金时间?
  3. phpunit 单元测试案例--签到任务
  4. SQL基础操作_2_操作多个表
  5. Exercise 12: Prompting People
  6. 微信小程序 全局变量异步函数_微信小程序【生命周期】
  7. JavaScript-Tool:jquery.qrcode.js
  8. STL中的关联式容器——set(集合)
  9. **尾座体工艺工装设计尾座体工艺工装设计**
  10. 小米MAX Root,第三方REC,XP框架刷入
  11. UWP,WPF 打包Roboto 字体
  12. 教你批量采集晨光文具的商品图片到电脑中
  13. 服务器尚未完成维护梦幻西游,梦幻西游维护公告
  14. 软考证书可以落户上海吗?很多人不知道
  15. 牛客小白月赛24 J—建设道路
  16. 人工智能导论(5)——搜索策略(Search Strategy)
  17. java频繁的读写文件_大量较为频繁读写的文件一般如何进行存储?
  18. Git的基本使用方法教程(入门级)
  19. 编程小白学python路线图_零基础Python学习路线图,让你少走弯路
  20. 【锻体篇-硬件开发】TL431可控精密稳压源的应用和注意事项

热门文章

  1. dataGrip导出sql脚本
  2. python遗传算法实例:求一元二次方程实例
  3. 计算机教室论文,高校多媒体教室计算机维护措施
  4. 苹果 iphone降级
  5. java开发实际工作中项目开发流程及岗位
  6. 【蓝桥杯单片机笔记】蓝桥杯备赛资料
  7. 「企业架构」企业架构师的TOGAF的权威指南
  8. 2019全球人才竞争力指数显示,华盛顿特区在城市排名中表现最出色
  9. VS Code CentOS7.7 终端字体设置
  10. c语言程序二进制代码,二进制搜索/查找程序(C语言)