聚聚视频讲的太好了
前六讲 最后三讲
题目全在这了
01揹包问题
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,v,w;i<=n;++i){
cin>>v>>w;
for(int j=m;j>=v;--j) dp[j]=max(dp[j],dp[j-v]+w);
}
cout<<dp[m]<<endl;
return 0;
}
完全揹包
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,v,w;i<=n;++i){
cin>>v>>w;
for(int j=v;j<=m;++j) dp[j]=max(dp[j],dp[j-v]+w);
}
cout<<dp[m]<<endl;
return 0;
}
多重揹包问题 I
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=105;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,v,w,s;i<=n;++i){
cin>>v>>w>>s;
for(int j=m;j>=v;--j){
for(int k=0;k<=s && k*v<=j;++k){
dp[j]=max(dp[j],dp[j-k*v]+k*w);
}
}
}
cout<<dp[m]<<endl;
return 0;
}
多重揹包问题 II
二进制拆分优化,然后作为01揹包来考虑即可,将s拆成2进制表示,最后多的那个部分单独拿出来。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=2e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
struct node{
int v,w;
};
vector<node> a;
int n,m,dp[N];
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,v,w,s;i<=n;++i){
cin>>v>>w>>s;
for(int k=1;k<=s;k*=2){
a.pb({v*k,w*k});
s-=k;
}
if(s) a.pb({v*s,w*s});
}
for(auto it:a){
int v=it.v,w=it.w;
for(int j=m;j>=v;--j) dp[j]=max(dp[j],dp[j-v]+w);
}
cout<<dp[m]<<endl;
return 0;
}
多重揹包问题 III
单调队列优化,对于一个体积为v的物品,我们考虑到他的更新,将体积%v的值进行分组,一个组之间才会有递推关系,而不同的组之间是不会有更新和递推关系的。考虑对于一个dp[j] 是从上一层dp[j-v]+w,dp[j-2*v]+2*w…….更新来,那对于dp[j+v]就是从上一层dp[j]+w,dp[j-v]+2*w…..更新而来,其实就是对于dp[j],dp[j+v]就是平移的一格,然后总体+w;但是原来数的大小关系并没有发生改变,所以这里用单调队列维护即可,只是加的x*w发生了改变,这个我们可以通过计算与j差几个v,就可以得出了嘛。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=2e5+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,dp[N][2],q[N];
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
bool fg=0;
int tt=-1,hh=0;
for(int i=1,v,w,s;i<=n;++i){
cin>>v>>w>>s;
for(int j=0;j<v;++j){//将体积按%v的值进行分类
tt=-1,hh=0;
for(int k=j;k<=m;k+=v){
dp[k][fg]=dp[k][fg^1];
while(hh<=tt && k-s*v>q[hh]) hh++;
if(hh<=tt) dp[k][fg]=max(dp[k][fg],dp[q[hh]][fg^1]+(k-q[hh])/v*w);
while(hh<=tt && dp[q[tt]][fg^1]-(q[tt]-j)/v*w <= dp[k][fg^1]-(k-j)/v*w) tt--;
q[++tt]=k;
}
}
fg^=1;
}
cout<<dp[m][fg^1]<<endl;
return 0;
}
混合揹包问题
即包含了01揹包,完全揹包和多重揹包,这里可以将多重揹包拆分成01揹包,然后我们只需要考虑01揹包和完全揹包,这里两个注意循环的方向即可。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],n,m;
struct node{
int type,v,w;
};
vector<node> a;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,v,w,s;i<=n;++i){
cin>>v>>w>>s;
if(s==-1) a.pb({-1,v,w});//01揹包
else if(s==0) a.pb({0,v,w});//多重揹包
else{//完全揹包拆成01揹包
for(int k=1;k<=s;k*=2){
a.pb({-1,v*k,w*k});
s-=k;
}
if(s) a.pb({-1,v*s,w*s});
}
}
int v,w;
for(auto it:a){
v=it.v,w=it.w;
if(it.type==-1){
for(int i=m;i>=v;--i) dp[i]=max(dp[i],dp[i-v]+w);
}
else{
for(int i=v;i<=m;++i) dp[i]=max(dp[i],dp[i-v]+w);
}
}
cout<<dp[m]<<endl;
return 0;
}
二维费用的揹包问题
多了一维重量,这里直接考虑多循环一层重量即可,01揹包从大到小进行更新即可。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N][N],n,V,M;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>V>>M;
for(int i=1,v,m,w;i<=n;++i){
cin>>v>>m>>w;
for(int j=V;j>=v;--j){
for(int k=M;k>=m;--k){
dp[j][k]=max(dp[j][k],dp[j-v][k-m]+w);
}
}
}
cout<<dp[V][M]<<endl;
return 0;
}
分组揹包问题
多一重循环,来判断选这一组里的哪一个
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e2+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,v[N],w[N],dp[N];
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,s;i<=n;++i){
cin>>s;
for(int j=1;j<=s;++j) cin>>v[j]>>w[j];
for(int j=m;j>=0;--j){
for(int k=1;k<=s;++k){
if(j>=v[k]) dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
}
}
}
cout<<dp[m]<<endl;
return 0;
}
有依赖的揹包问题
dp[i][j]:当选i为节点,空间不大于j的最大值
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e2+5;
//il int Add(ll &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N][N];//dp[i][j]:当选i为节点,空间不大于j的最大值
int n,m,v[N],w[N],root;
vector<int> G[N];
il void dfs(int u){
for(auto son:G[u]){
dfs(son);
for(int j=m-v[u];j>=0;--j){//给u空出v[u]空间,剩下的分给儿子
for(int k=0;k<=j;++k){
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[son][k]);
}
}
}
for(int j=m;j>=v[u];--j) dp[u][j]=dp[u][j-v[u]]+w[u];
for(int j=v[u]-1;j>=0;--j) dp[u][j]=0;
}
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1,np;i<=n;++i){
cin>>v[i]>>w[i]>>np;
if(np==-1) root=i;
else G[np].pb(i);
}
dfs(root);
cout<<dp[root][m]<<endl;
return 0;
}
揹包问题求方案数
这里初始化不能随便的默认为0了,因为这里需要更新方案数,前面的dp[i]表示体积不大于i的最大价值,(这和全部初始化为0有关,因为如果当空间为k时取得最大价值,因为我们将dp[m-k]初始化为0,所以也可以得到最值,但实际空间不为m)这样的话不利于更新方案数。现在初始话只将dp[0]定义为0,其他的定义为-inf,dp[i]表示空间恰好为i时的最大价值,这样是符合实际情况的,最后的mx是要扫一遍数组的,因为最大价值并不一定在最大空间取得。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int mod=1e9+7;
const int N=1e3+5;
il int Add(int &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(int &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int dp[N],num[N];
//dp[i]:体积为i时的最大价值,num[i]:满足体积为i最大价值的方案数
int n,m;
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
num[0]=1;
for(int i=1;i<=m;++i) dp[i]=-inf;
for(int i=1,v,w;i<=n;++i){
cin>>v>>w;
for(int j=m;j>=v;--j){
int mx,mxnum=0;
mx=max(dp[j],dp[j-v]+w);
if(mx==dp[j]) Add(mxnum,num[j]);
if(mx==dp[j-v]+w) Add(mxnum,num[j-v]);
dp[j]=mx,num[j]=mxnum;
}
}
int ans=-inf,all=0;
for(int j=0;j<=m;++j) ans=max(ans,dp[j]);
for(int j=0;j<=m;++j){
if(dp[j]==ans) Add(all,num[j]);
}
cout<<all<<endl;
return 0;
}
揹包问题求具体方案
因为题目要求字典序,所以i从大到小着来,然后从1到n从小到大贪心的考虑。视频中代码少加了一个nv-v[i]的判断。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define SZ(a) int((a).size())
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int N=1e3+5;
//il int Add(int &x,ll y) {return x=x+y>=mod?x+y-mod:x+y;}
//il int Mul(int &x,ll y) {return x=x*y>=mod?x*y%mod:x*y;}
int n,m,dp[N][N],v[N],w[N];
int main(){
std::ios::sync_with_stdio(0);cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>v[i]>>w[i];
//从后往前更新
for(int i=n;i>=1;--i){
for(int j=0;j<=m;++j){
if(j>=v[i]) dp[i][j]=max(dp[i+1][j],dp[i+1][j-v[i]]+w[i]);
else dp[i][j]=dp[i+1][j];
}
}
int nv=m;
for(int i=1;i<=n;++i){
if(nv-v[i]>=0 && dp[i][nv]==dp[i+1][nv-v[i]]+w[i]){
cout<<i<<" ";
nv-=v[i];
}
}
return 0;
}