01背包
2. 01背包问题 - AcWing题库
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int res;
int mem[N][N];
int dfs(int x,int spv)
{
if(mem[x][spv]) return mem[x][spv];
if(x>n) return mem[x][spv]=0;
if(spv>=v[x])
{
return mem[x][spv]=max(dfs(x+1,spv),dfs(x+1,spv-v[x])+w[x]);
}else return mem[x][spv]=dfs(x+1,spv);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
res=dfs(1,m);
cout<<res;
return 0;
}
这里补个图,DFS是自顶向下推的
dp的递推是从下往上
倒序递推
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int res;
int mem[N][N];
int f[N][N];
int dfs(int x,int spv)
{
if(mem[x][spv]) return mem[x][spv];
if(x>n) return mem[x][spv]=0;
if(spv>=v[x])
{
return mem[x][spv]=max(dfs(x+1,spv),dfs(x+1,spv-v[x])+w[x]);
}else return mem[x][spv]=dfs(x+1,spv);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
// res=dfs(1,m);
// cout<<res;
// 倒序递推f[i][j]
// 从第i个物品开始,选总体积<=j的物品的总价值的最大值
for(int i=n;i>=1;i--)
{
for(int j=0;j<=m;j++)
{
if(j<v[i]) f[i][j]=f[i+1][j];
else f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i]);
}
}
cout<<f[1][m];
return 0;
}
正序递推
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int res;
int mem[N][N];
int f[N][N];
int dfs(int x,int spv)
{
if(mem[x][spv]) return mem[x][spv];
if(x<1) return mem[x][spv]=0;
if(spv>=v[x])
{
return mem[x][spv]=max(dfs(x-1,spv),dfs(x-1,spv-v[x])+w[x]);
}else return mem[x][spv]=dfs(x-1,spv);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
// res=dfs(n,m);
// cout<<res;
// 正序递推f[i][j]
// 从前i个物品中选,选总体积<=j的物品的总价值的最大值
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j<v[i]) f[i][j]=f[i-1][j];
else f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
}
}
cout<<f[n][m];
return 0;
}
空间优化递推
用一维数组代替二维数组
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int res;
int f[N],g[N];
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j<v[i]) g[j]=f[j];
else g[j]=max(f[j],f[j-v[i]]+w[i]);
}
memcpy(f,g,sizeof(f));
}
cout<<f[m];
return 0;
}
进一步优化
这里m从m走到0,的原因是只让每个物品最多拿一次。
如果正序枚举体积,就会让物品被拿多次,从而违反规则,
但完全背包不用考虑这个问题,因为它本身就能拿多次~
所以完全背包优化到一维可以正序枚举。
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int res;
int f[N];
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
//if(j<v[i]) f[j]=f[j];//可以省略
if(j>=v[i]) f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout<<f[m];
return 0;
}
二维费用背包问题(本质是01背包)
8. 二维费用的背包问题 - AcWing题库
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,cap,we;
int v[N],m[N],w[N];
int mem[N][110][110];
int dfs(int x,int spv,int spm)
{
if(mem[x][spv][spm]) return mem[x][spv][spm];
if(x>n) return 0;
if(spv>=v[x]&&spm>=m[x]) return mem[x][spv][spm]=max(dfs(x+1,spv,spm),dfs(x+1,spv-v[x],spm-m[x])+w[x]);
else return mem[x][spv][spm]=dfs(x+1,spv,spm);
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>cap>>we;
for(int i=1;i<=n;i++) cin>>v[i]>>m[i]>>w[i];
int res=dfs(1,cap,we);
cout<<res;
return 0;
}
倒序递推
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,cap,we;
int v[N],m[N],w[N];
int mem[N][110][110];
int f[N][110][110];
int dfs(int x,int spv,int spm)
{
if(mem[x][spv][spm]) return mem[x][spv][spm];
if(x>n) return 0;
if(spv>=v[x]&&spm>=m[x]) return mem[x][spv][spm]=max(dfs(x+1,spv,spm),dfs(x+1,spv-v[x],spm-m[x])+w[x]);
else return mem[x][spv][spm]=dfs(x+1,spv,spm);
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>cap>>we;
for(int i=1;i<=n;i++) cin>>v[i]>>m[i]>>w[i];
// int res=dfs(1,cap,we);
// cout<<res;
for(int i=n;i>=1;i--)
{
for(int j=0;j<=cap;j++)
{
for(int k=0;k<=we;k++)
{
if(j<v[i]||k<m[i])
{
f[i][j][k]=f[i+1][j][k];
}
else f[i][j][k]=max(f[i+1][j][k],f[i+1][j-v[i]][k-m[i]]+w[i]);
}
}
}
cout<<f[1][cap][we];
return 0;
}
正序+二维优化
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,cap,we;
int v[N],m[N],w[N];
int mem[N][110][110];
int f[110][110];
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>cap>>we;
for(int i=1;i<=n;i++) cin>>v[i]>>m[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=cap;j>=v[i];j--)
{
for(int k=we;k>=m[i];k--)
{
f[j][k]=max(f[j][k],f[j-v[i]][k-m[i]]+w[i]);
}
}
}
cout<<f[cap][we];
return 0;
}
完全背包
3. 完全背包问题 - AcWing题库
记忆化搜索
完全背包 和 01背包唯一不同就在于如果当前这个物品可以装入,在下一次选择中x不用+1(因为物品是无限多的,还可以再次选择)
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int mem[N][N];
int dfs(int x,int spv)
{
if(mem[x][spv]) return mem[x][spv];
if(x>n) return 0;
if(spv<v[x]) return mem[x][spv]=dfs(x+1,spv);
else return mem[x][spv]=max(dfs(x+1,spv),dfs(x,spv-v[x])+w[x]);
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
int res=dfs(1,m);
cout<<res;
return 0;
}
正序递推
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j<v[i]) f[i][j]=f[i-1][j];
else f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
}
}
cout<<f[n][m];
return 0;
}
优化成一维数组
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
{
for(int j=v[i];j<=m;j++)
{
f[j]=max(f[j],f[j-v[i]]+w[i]) ;
}
}
cout<<f[m];
return 0;
}
01 背包优化到一维 :逆序枚举体积
完全背包优化到一维 :正序枚举体积
习题
云剪贴板 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
NASA的食物计划(二维费用背包问题)
P1507 NASA的食物计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=60;
int hh,tt,n;
int h[N],t[N],k[N];
int mem[55][410][510];
int dfs(int x,int sph,int spt)
{
if(x>n) return 0;
if(mem[x][sph][spt]) return mem[x][sph][spt];
if(sph>=h[x]&&spt>=t[x])
{
return mem[x][sph][spt]=
max(dfs(x+1,sph,spt),dfs(x+1,sph-h[x],spt-t[x])+k[x]);
}else return mem[x][sph][spt]=dfs[x+1][sph][spt];
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>hh>>tt>>n;
for(int i=1;i<=n;i++) cin>>h[i]>>t[i]>>k[i];
int res=dfs(1,hh,tt);
cout<<res;
return 0;
}
优化后的二维数组递推
#include<bits/stdc++.h>
using namespace std;
const int N=60;
int hh,tt,n;
int h[N],t[N],k[N];
int f[410][510];
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>hh>>tt>>n;
for(int i=1;i<=n;i++) cin>>h[i]>>t[i]>>k[i];
for(int i=1;i<=n;i++)
{
for(int j=hh;j>=h[i];j--)
{
for(int l=tt;l>=t[i];l--)
{
f[j][l]=max(f[j][l],f[j-h[i]][l-t[i]]+k[i]);
}
}
}
cout<<f[hh][tt];
return 0;
}
01背包求体积恰好为M的方案数
278. 数字组合 - AcWing题库
记忆化搜索
Time Limit Exceeded
要优化成递推
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int a[N];
int mem[N][10010];
int dfs(int x,int spm)
{
if(spm==0) return 1;
if(x>n) return 0;
if(mem[x][spm]) return mem[x][spm];
if(spm>=a[x]) return mem[x][spm]=dfs(x+1,spm-a[x])+dfs(x+1,spm);
else return mem[x][spm]=dfs(x+1,spm);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
int res=dfs(1,m);
cout<<res;
return 0;
}
一维数组递推
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int a[N];
int f[10010];//记录方案数
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
f[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=a[i];j--)
{
f[j]=f[j-a[i]]+f[j];
}
}
cout<<f[m];
return 0;
}
完全背包求体积恰好为M的方案数
OpenJudge - 6049:买书
记忆化搜索
这个就能ac了
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
int mon[5]={0,10,20,50,100};
int mem[N][N];
int dfs(int x,int spn)
{
if(spn==0) return 1;
if(x>4) return 0;
if(mem[x][spn]) return mem[x][spn];
if(spn>=mon[x]) return mem[x][spn]=dfs(x+1,spn)+dfs(x,spn-mon[x]);
else return mem[x][spn]=dfs(x+1,spn);
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
if(n==0)
{
cout<<"0";
return 0;
}
int res=dfs(1,n);
cout<<res;
return 0;
}
优化一下
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n;
int mon[5]={0,10,20,50,100};
int f[N];
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
if(n==0)
{
cout<<"0";
return 0;
}
f[0]=1;
for(int i=1;i<=4;i++)
{
for(int j=mon[i];j<=n;j++)
{
f[j]=f[j]+f[j-mon[i]];
}
}
cout<<f[n];
return 0;
}
背包问题求具体方案
12. 背包问题求具体方案 - AcWing题库
采药
P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
疯狂的采药
P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
5倍经验日
P1802 5 倍经验日 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
宠物小精灵之收服
OpenJudge - 4978:宠物小精灵之收服