背包问题
01背包问题:
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N]; int f[N][N]; int main() { int n, m; 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++) { 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]); } cout<<f[n][m]<<endl; return 0; }
一维:
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N]; int f[N]; int main() { int n, m; 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 >= v[i]; j--) { f[j] = max(f[j], f[j-v[i]] + w[i]); } cout<<f[m]<<endl; return 0; }
转化为一维滚动数组的做法:
f[i][j]只和 i - 1的状态有关,所以可以只用第二维的状态来表示。
如果
j = 0; j <= m; j++
j 这样递减的话,那么f[j-v[i])肯定就在前面的f[j]当中就已经得出来了,重复了,然后这个f[j - v[i]]表示的其实是f[i][j-v[i]]的状态对应的是f[i][j](上一次的j等于此阶段的f[j - v[i]],因为j这个维度如果还是递增的话,那么f[j] = max(f[j], f[j-v[i]] + w[i]) 等价于 f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]; 事实上正确的应该是f[i][j] = max(f[i][j], f[i-1][j-v[i]] + w[i])。所以要把j这个容量的维度循环改为递减。
int j = m; j >= v[i]; j--
这样f[j]不会覆盖,f[j-v[i]]就还没有被更新过,表示的也是下一个i - 1的阶段,即:f[i-1][j-v[i]]的值。
2 2 2 2 2 6 6 6 4 8 6 6 8 6 8
2、完全背包问题
f[i][j] = f[i-k*v[i]] + k * w[i];
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N]; int f[N][N]; int main() { int n, m; 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++) for(int k = 0; k * v[i] <= j; k++) { f[i][j] = max(f[i][j], f[i-1][j-k*v[i]] + k * w[i]); } cout<<f[n][m]<<endl; return 0; }
f[i][j] = max(f[i - 1][j], f[i-1][j-v]+w, f[i-1][j-2v]+2w,f[i-1][j-3v]+3w,.......)
f[i,j-v] = max( f[i-1][j-v], f[i-1][j-2v] + w, f[i-1][j-3v] + 2w,.......)
完全背包:f[i,j] = max(f[i - 1, j], f[i][j - v[i] +w[i]]),
01背包:f[i, j] = max(f[i-1, j], f[i-1][j-v[i]+w[i]);
把k的第三层循环去掉,
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N]; int f[N][N]; int main() { int n, m; 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++){ f[i][j] = f[i-1][j]; if(j >= v[i]) f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]); } cout<<f[n][m]<<endl; return 0; }
同样把完全背包优化由二维优化为一维:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 1010; int v[N], w[N]; int f[N]; int main() { int n, m; 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]<<endl; return 0; }
这里就没有01背包的烦忧了,背包的容量m不需要递减遍历了。因为j是从小到大枚举的,然后f[j-v[j]]肯定在前面的f[j]就枚举出现过,然后第一维代表的肯定是i,这样就跟完全背包的状态计算完全一样:f[i][j] = max(f[i - 1][j], f[i][j-v[i]] + w[i]); 没有计算过的就是下一个层次的。
多重背包:
f[i][j] = max(f[i-1][j-v[i]*k] + k * w[i]);k =0,1,2,……,s[i]朴素版本的完全背包问题
#include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N = 110; int v[N],w[N],f[N][N],s[N]; int main() { int n,m; cin>>n>>m; for(int i = 1;i <= n;i++) cin>>v[i]>>w[i]>>s[i]; for(int i = 1; i <= n;i++) for(int j = 0; j <= m; j++) for(int k = 0; k <= s[i] && k * v[i] <= j; k++){ f[i][j] = max(f[i][j], f[i - 1][j-k*v[i]] + k * w[i]); } cout<<f[n][m]<<endl; }
优化:先同上面的完全背包一样
f[i,j] = max(f[i-1,j],f[i-1][j-v]+w,f[i-1][j-2v]+2w,...,f[i-1, j-sv]+sw);
f[i-1,j-v] = max( f[i-1][j-v], f[i-1][j-2v]+w,... f[i-1,j-sv]+(s-1)w), f[i-1, j - (s+1)v] + sw);
不能用前面的类似完全背包的优化方式
二进制优化方式:
s = 200, 1, 2, 4, 8, 16, 32, 64, + 73
0~127 73~200 这样就可以凑出0 ~ 200
物品数量就可以从s降到logs,把物品的个数二进制分解,就不用从0枚举到s了。而是用logs个二进制数再加一个常数c就可以把数量s完全的表示出来。
原来的时间复杂度:N * V * S 变成对NlogS的物品做01背包 : N * V * logS = 1000 * 2000 * 12 = 2.4e7
多重背包的优化版本:
#include <iostream> #include <algorithm> using namespace std; const int N = 24000, M = 2010; int n, m; int v[N], w[N]; int f[N]; int main() { cin>>n>>m; int cnt = 0; for(int i = 1; i <= n;i++) { int a, b, s; cin>>a>>b>>s; int k = 1; while(k <= s) { cnt++; v[cnt] = a * k; w[cnt] = b * k; s -= k; k <<= 1; } if(s > 0) { cnt++; v[cnt] = a * s; w[cnt] = b * s; } } n = cnt; for(int i = 1; i <= n; i++) for(int j = m; j >= v[i]; j--) { f[j] = max(f[j], f[j-v[i]] + w[i]); } cout<<f[m]<<endl; }
分组背包问题:
有N组,每组有若干的物品,有对应的体积和价值,每组只能选其中一样的物品。
如果第i组物品不选的话,f[i - 1, j].
如果选了第 i 组第 k 个物品的话:f[i - 1, v[i, k]] + w[i, k].
#include <iostream> #include <algorithm> using namespace std; const int N = 110; int v[N][N], w[N][N], s[N], f[N]; int main() { int n,m; cin>>n>>m; for(int i = 1;i <= n; i++) { cin>>s[i]; for(int j = 0;j < s[i]; j++) cin>>v[i][j]>>w[i][j]; } for(int i = 1; i <= n; i++) for(int j = m; j >= 0; j--) for(int k = 0; k < s[i]; k++) if(j >= v[i][k]) f[j] = max(f[j], f[j-v[i][k]] + w[i][k]); cout<<f[m]<<endl; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?