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题库
#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int f[N][N];
int v[N],w[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=n;i;i--)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i+1][j];//不选
if(j>=v[i]) f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);
}
}
int i=1,j=m;
while(i<=n)
{
if(j>=v[i]&&f[i+1][j-v[i]]+w[i]>=f[i+1][j])
//如果可以选并且选了之后方案数会变多
{
cout<<i<<" ";//那就选择
j-=v[i];//体积减少
i++;
}
else i++;
}
return 0;
}
采药
P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int tt,m;
int t[N],v[N];
int mem[110][1010];
int dfs(int x,int spt)
{
if(x>m) return 0;
if(mem[x][spt]) return mem[x][spt];
if(t[x]<=spt)
{
return mem[x][spt]=max(dfs(x+1,spt),dfs(x+1,spt-t[x])+v[x]);
}else return mem[x][spt]=dfs(x+1,spt);
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>tt>>m;
for(int i=1;i<=m;i++) cin>>t[i]>>v[i];
int res=dfs(1,tt);
cout<<res;
return 0;
}
优化后
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int tt,m;
int t[N],v[N];
int f[1010];
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>tt>>m;
for(int i=1;i<=m;i++) cin>>t[i]>>v[i];
for(int i=1;i<=m;i++)
{
for(int j=tt;j>=t[i];j--)
{
f[j]=max(f[j],f[j-t[i]]+v[i]);
}
}
cout<<f[tt];
return 0;
}
疯狂的采药
P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
没别的,这题要注意数据范围
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
const int M=1e7+10;
#define int long long
int tt,m;
int t[N],v[N];
int f[M];
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>tt>>m;
for(int i=1;i<=m;i++) cin>>t[i]>>v[i];
for(int i=1;i<=m;i++)
{
for(int j=t[i];j<=tt;j++)
{
f[j]=max(f[j-t[i]]+v[i],f[j]);
}
}
cout<<f[tt];
return 0;
}
5倍经验日
P1802 5 倍经验日 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
仔细读题 文章来源:https://www.toymoban.com/news/detail-435358.html
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
#define int long long
int n,x;
int l[N],w[N],u[N];
int mem[N][N];
int f[N];
signed main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) cin>>l[i]>>w[i]>>u[i];
for(int i=1;i<=n;i++)
{
for(int j=x;j>=0;j--)
{
if(j>=u[i]) f[j]=max(f[j]+l[i],f[j-u[i]]+w[i]);//服药能打过就选择吃药或者不吃
else f[j]=f[j]+l[i];
}
}
cout<<5*f[x];
return 0;
}
宠物小精灵之收服
OpenJudge - 4978:宠物小精灵之收服文章来源地址https://www.toymoban.com/news/detail-435358.html
#include <bits/stdc++.h>
using namespace std;
int dp[1005][505];//dp[i][j][k]:
//(第一维i被优化掉)前i个野生小精灵中最多使用j个精灵球,
//k点皮卡丘的体力值,能捕捉到的最大小精灵数量。
int b[105], d[105];//b[i]:收第i小精灵需要的精灵球球数
//d[i]:收第i小精灵皮卡丘损失的体力值
int main()
{
int n, m, ka, r;
cin >> n >> m >> ka;//n个精灵球 皮卡丘m点体力值 野生小精灵有ka个
m--;//不能把m点体力值都用完,最多可以使用m-1点体力
for(int i = 1; i <= ka; ++i)
cin >> b[i] >> d[i];
for(int i = 1; i <= ka; ++i)
for(int j = n; j >= b[i]; --j)
for(int k = m; k >= d[i]; --k)
dp[j][k] = max(dp[j][k], dp[j-b[i]][k-d[i]]+1);//记录精灵球个数
for(int k = 0; k <= m; ++k)//用掉的体力值可以为0,从0开始遍历
{
if(dp[n][k] == dp[n][m])//d[n][m]收服的数量肯定是最多的
{//一旦前面有一个跟dp[n][m]收服的数量相同,就更新答案
r = m + 1 - k ;//原总体力值为m+1,减去用掉的体力j
break;
}
}
cout << dp[n][m] << ' ' << r;
return 0;
}
到了这里,关于【DP】学习之背包问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!