部分背包
背包问题是贪心和动态规划的经典问题,这里学习一下部分背包问题的解法。
部分背包问题常用贪心解决,其贪心策略为优先选取单位重量价值最大的商品(介绍完算法再来看一下为什么),下面先看一下主要算法。
这个算法也比较简单,优先选择单位重量价值的商品,用x【】数组记录是把该商品全部拿走还是只取部分即可。
至于Sort函数,需要自己写,后面的代码有,可以简单看一下。
以上问题笔者经过计算,结果分别为:
(1)选取商品为:D + F + B + E + 35/60*C
重量:50 +10 +30 +25 +35 = 150
价值:50 + 40 + 40 +30 +30 + 17.5 = 167.5
(2)选取商品为:F +G + B + A +E +10/50*D
重量:10 + 25 +30 + 35 +40 +10 = 150
价值:40 + 30 +40 +10 + 35 +10 = 165
(3)选取商品为:F + B + G + D + 35/40*E
重量:10 + 30 + 25 +50 +35 = 150
价值: 40 +40 +30 + 50 +30.625 = 190.625
显然,以上三种贪心策略中,每次选取单位重量价值最大的物品装入背包可得到最大价值!
(为了证明算法的正确性,还必须证明背包问题具有贪心选择性质。)
部分背包问题的贪心策略的正确性证明
贪心策略是:总是优先选择单位重量下价值最大的物品
正确性证明 是:使用该贪心策略,可以获得最优解。在这里,最优解就是带走的物品价值最大。
证明思路:先考察一个全局最优解,然后对该解加以修改(一般是采用“剪枝”技巧),使其采用贪心选择,这个选择将原问题变成一个相似的、但是更小的问题。
先假设 物品集合S={W1,W2....Wn}已经按 单位重量价值从小到大排好序了。
并假设 一个全局最优解是:S(i)={Wi1,Wi2,.....Win}。Wi1,Wi2,.....Win是有序的。对于贪心选择而言,总是会优先 选择 Wn 的物品,当Wn 没有后,再选择Wn-1 .....
如果Win = Wn 问题已经得证。因为,我们的最优解S(i)中,已经包含了贪心选择。只要继续归纳下去,Wi(n-1) 就是 Wn-1 ....
如果Win != Wn 运用剪枝技巧,剪掉Win 并 贴上 Wn 此时,得到的是一个更优的解(因为价值更大了 ,Wn > Win)。因为,Wn 是单位重量价值最高的那个物品啊,我们的贪心选择应该选择它,但是这里的最优解S(i)却没有选择它,于是我们用剪枝技巧,将它加入到S(i)中去,并把S(i)中的Win除去。
就证明了,如果用贪心策略来进行选择,得到的是最优解。从而证明了贪心算法的正确性。
其实,也就是证明了一定存在一个最优解,这个最优解就是由贪心选择组成的。
代码的实现
ok,废话bb一大堆,上代码让大家瞧瞧
#include<bits/stdc++.h> using namespace std; const int maxn = 1000; float z[maxn]; void Sort(int n,float w[],float v[]){ for(int i=0;i<n;i++) z[i]=v[i]/w[i];//用z[]存商品的单位重量价值 for(int i=0;i<n;i++){//此排序的策略是每次把 单位重量商品的价值最大 的商品放在前面 for(int j=i+1;j<n;j++){ if(z[i]<z[j]){ float temp = z[i]; z[i] = z[j]; z[j]=temp; float tempw = w[i];//这里不要忘记把商品的重量和价值同时放到最前面 w[i] = w[j]; w[j] = tempw; float tempv = v[i]; v[i] = v[j]; v[j] = tempv; } } } } void fire(int n,float w[],float v[],float x[],float carrymax){ Sort(n,w,v);//根据单位重量商品的价值对商品进行排序 int i; for(i=0;i<n;i++){ if(w[i]>carrymax) break; x[i] = 1; //x[]数组用来记录此次是否选择商品,1代表全部拿走,0代表不拿,小数代表部分拿 carrymax -= w[i]; } if(i<=n-1) x[i] = carrymax/w[i]; } int main(){ int n; float carry=0; float carrymax,v[maxn],w[maxn],x[maxn];//w[],每个商品的重量,v[]代表每个商品的价值,carrymax代表最大容量 memset(x,0,sizeof(x)); cout<<"请输入最大容量:"; cin>>carrymax; cout<<"请输入商品数量:"; cin>>n; cout<<"请输入每个商品的重量和价值:"<<endl; for(int i=0;i<n;i++){ cin>>w[i]>>v[i]; } fire(n,w,v,x,carrymax); for(int i=0;i<n;i++){ if(x[i]==1){ cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品全部拿走"<<endl; carry+=v[i]; } else if(x[i]==0) cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品不拿走"<<endl; else{ cout<<"商品重量为"<<w[i]<<" 价值为"<<v[i]<<"的商品拿走"<<x[i]<<endl; carry+=v[i]*x[i]; } } cout<<"最终收获的商品价值为:"<<carry<<endl; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现