聚聚視頻講的太好了
前六講 最後三講
題目全在這了
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;
}