--- title: "01背包问题" created: 2025-11-28 tags: - 算法 --- # 01背包问题 ## 题目 [01背包问题](https://www.acwing.com/problem/content/2/) ![[image-0c98de0f.png]] ## 思路分析 有了前两道题的引入 现在尝试一下01背包 拿到这个问题的时候先会有一种想法: 把每个物品排一下序 因为有个体积v还有个价值w 那把w/v就是性价比 把它排个序 然后用类似贪心的思想去写(每次选性价比最高的) 咋一看很有道理 但其实是不对的 比如 n=2 v=5 (1)v=5 w=5 性价比:1 (2)v=3 w=4 性价比:4/3 貌似(2)的性价比更高 但实际答案应该是(1) 这其实就是一个全局最优和局部最优的问题 (很欣慰的是前面刚悟到了Dijkstra贪心局部最优和spfa的考虑dp全局最优的区别 在这里又印证了 不禁感慨 y总真的是tql) 对于这种考虑大局的问题 要使用dp去写 用贪心的话就显得目光短浅了 不扯了 先分析这题 老规矩 还是先暴力去写 然后想办法改成dp **暴搜:** 不外乎就是对于每个物品 有选和不选两种方案 与前面不同的是 此时的节点存放的是背包剩余体积和价值 而边表示对于每个物品选与不选(往左不选往右选) 然后孩子结点就是执行选或不选操作后 背包的新状态 那么显而易见 使用dfs可以得到所有答案 其中就可以得到最优解 ![[image-e3349f67.png]] 从第一个物品开始选 初始的剩余空间spv为m 对于每个物品有选和不选两种方案 当背包剩余空间不够时(spv using namespace std; const int N=1010; int v[N],w[N]; int n,m; int dfs(int x,int spV) { if(x>n) return 0; //容量不够放下该物品时 只能跳过该物品继续往后走 else if(spV=v[x]) //如果放了 容积就会减小 且背包内价值会增大 return max(dfs(x+1,spV),dfs(x+1,spV-v[x])+w[x]); } int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; int res=dfs(1,m); cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int mem[N][N];//记忆化数组(两个参数所以二维) int dfs(int x,int spV) { if(mem[x][spV]) return mem[x][spV]; int sum=0; if(x>n) sum = 0; else if(spV=v[x]) sum = max(dfs(x+1,spV),dfs(x+1,spV-v[x])+w[x]); mem[x][spV]=sum; return sum; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; int res=dfs(1,m); cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; //递归搜索树里树叶是较大 树根是较小 所以n~1往前推 for(int i=n;i>=1;i--) { //遍历体积 for(int j=0;j<=m;j++) { if(j=v[i])//如果放得下 就要取选或不选 结果更大的那种 { f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i]); } } } cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; //第0个物品无需考虑 f[0][0~m]的最大价值永远是0 初始化成0(全局) //从第1个物品开始考虑 for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ //还是一样的 左边的集合是一定存在的(不选) //右边的集合有可能是空集 (剩余体积不够时) f[i][j]=f[i-1][j]; if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]); } } cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N]; int main() { 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--) //因为 j-v[i] using namespace std; const int N=1010; int v[N],w[N]; int n,m; int dfs(int x,int spV) { if(x>n) return 0; //容量不够放下该物品时 只能跳过该物品继续往后走 else if(spV=v[x]) //如果放了 容积就会减小 且背包内价值会增大 return max(dfs(x+1,spV),dfs(x+1,spV-v[x])+w[x]); } int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; int res=dfs(1,m); cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int mem[N][N];//记忆化数组(两个参数所以二维) int dfs(int x,int spV) { if(mem[x][spV]) return mem[x][spV]; int sum=0; if(x>n) sum = 0; else if(spV=v[x]) sum = max(dfs(x+1,spV),dfs(x+1,spV-v[x])+w[x]); mem[x][spV]=sum; return sum; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; int res=dfs(1,m); cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; //递归搜索树里树叶是较大 树根是较小 所以n~1往前推 for(int i=n;i>=1;i--) { //遍历体积 for(int j=0;j<=m;j++) { if(j=v[i])//如果放得下 就要取选或不选 结果更大的那种 { f[i][j]=max(f[i+1][j],f[i+1][j-v[i]]+w[i]); } } } cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N][N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; //第0个物品无需考虑 f[0][0~m]的最大价值永远是0 初始化成0(全局) //从第1个物品开始考虑 for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ //还是一样的 左边的集合是一定存在的(不选) //右边的集合有可能是空集 (剩余体积不够时) f[i][j]=f[i-1][j]; if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]); //f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);(前面有 f[i][j]=f[i-1][j];) } } cout< using namespace std; const int N=1010; int v[N],w[N]; int n,m; int f[N]; int main() { 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--) //因为 j-v[i]