背包问题Word文档格式.docx
- 文档编号:22678991
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:31
- 大小:27.48KB
背包问题Word文档格式.docx
《背包问题Word文档格式.docx》由会员分享,可在线阅读,更多相关《背包问题Word文档格式.docx(31页珍藏版)》请在冰豆网上搜索。
方法:
贪心法
先计算所有物品的单位价值(即P[i]/W[i]的值),这个值越大,表明该物品的单位价值越大,那越是应该优先选择装入背包中。
此处的贪心策略是可以通过“反证法”严格证明的。
但并不是所有题目的贪心策略都可以方便地证明的,有些看似正确,其实贪心法并不能得到正确的解。
在有限的竞赛时间内,一般都是通过尝试不断找反例来验证自己的贪心策略的正确性。
Programex1a;
Constmaxn=100;
Varp,w:
array[1..maxn]ofreal;
temp,max,total:
real;
n,i,j:
integer;
Begin
Readln(n,max);
{读入物品数及背包容量}
Fori:
=1tondo{读入每件物品的重量、价值}
Readln(w[i],p[i]);
fori:
=1ton-1do{按单位价值降序排列}
forj:
=i+1tondo
ifp[i]*w[j]<
p[j]*w[i]then
begin
temp:
=p[i];
p[i]:
=p[j];
p[j]:
=temp;
=w[i];
w[i]:
=w[j];
w[j]:
end;
total:
=0;
=1tondo{从单位价值大的开始,贪心选取物品}
ifw[i]<
=maxthenbegintotal:
=total+p[i];
max:
=max-w[i];
end{可以全取}
elsebegintotal:
=total+p[i]/w[i]*max;
break;
end;
{只能取部分}
writeln(total:
0:
2);
end.
[测试数据]
输入:
5100
102
203
303
405
506
输出:
13.6
第2节0-1背包
所谓0-1背包,就是指一件物品是不可分的,要么不取(0)、要么全取
(1)。
根据每件物品的数量多少,0-1背包可以再分为0-1有限背包(每件物品只有1个)和0-1无限背包(完全背包),有时还有所谓的多重背包(即每件物品都有K[i]个)。
一、0-1有限背包
1、求最多可放入的体积
例2、装箱问题
[问题描述]
有一个箱子容量为maxv(正整数,0≤maxv≤20000),同时有n件物品(0≤n≤30),每件物品有一个体积vi(正整数)和一个价值pi(正整数)。
要求从这n件物品中任取若干件装入箱内,使箱子的剩余空间最小。
[输入样例]
1003{maxv,n}
704020{v1,v2,……,vn}
[样例输出]
10
方法1:
搜索
Programex2a;
constmaxn=31;
vari,best,n,maxv:
v,s:
array[0..maxn]ofinteger;
{设s[n]为前n件物品的体积和}
proceduresearch(k,restv:
integer);
{搜索第k个物品,当前剩余空间为restv}
begin
ifrestv<
bestthenbest:
=restv;
ifrestv-(s[n]-s[k-1])>
=bestthenexit;
{如果把剩下的全部放进去,得到的解也不会比当前的更优,那么就剪枝}
ifk<
=nthenbegin
ifrestv>
=v[k]thensearch(k+1,restv-v[k]);
{如果第k件物品可以放进去,那么就递归放第k+1件物品}
search(k+1,restv);
{如果第k件物品不放,则对第k+1件物品递归}
begin{main}
readln(maxv,n);
=1tondoread(v[i]);
best:
=maxv;
{best用来记录最后的答案,初始化为箱子的容量}
fillchar(s,sizeof(s),0);
=1tondo
s[i]:
=s[i-1]+v[i];
{预处理}
search(1,maxv);
{从第1件物品开始搜,当前剩余体积为maxv}
writeln(best);
方法2:
动态规划
设F[i,j]为一布尔型数组,表示在前i件物品中选择若干个放入箱子,其占用的体积正好为j的情况。
问题的解为maxv减去所有满足F[n,j]=true的j的最大值。
这样,就将一个最优化问题转化为一个判定性问题。
而:
F[i,j]=F[i-1,j-v[i]](v[i]<
=j<
=maxv)
边界:
F[0,0]:
=true
以上为未经优化的二维动态规划,主要的程序代码如下:
f[0,0]:
=true;
=maxvdowntov[i]do{注意与后面无限背包的区别}
f[i,j]:
=f[i-1,j]orf[i-1,j-v[i]];
=maxvdownto1do
iff[n,i]thenbegin
=maxv-i;
break;
F[i,j]的变化:
由上一行往下一行逐步推,红色的T表示值的变化。
j=0
1
…
20
30
40
60
70
90
100
i=0
T
F
FT
2
3(n)
观察发现,其实当前状态只与前一阶段状态有关,可以降至一维数组实现。
即用F[i]表示任意选取若干件物品,占用体积为i的情况可否实现,实现方法如下:
F[0]:
Fori:
Forj:
=maxvdowntov[i]do{注意与后面无限背包的区别}
F[j]:
=F[j]orF[j-v[i]];
参考程序如下:
Programex2b;
vari,j,n,maxv:
v:
f:
array[0..20000]ofboolean;
=1tomaxvdof[i]:
=false;
f[0]:
=maxvdowntov[i]do
f[j]:
=f[j]orf[j-v[i]];
iff[i]then
beginwriteln(maxv-i);
halt;
105
12345
2、求可以放入的最大价值
[算法分析]
Programex2c;
v,p,s:
{设s[i]为前i件物品的总价值}
proceduresearch(k,restv,ps:
{搜索第k件物品,当前剩余空间为restv,当前总价值为ps}
ifps>
=bestthenbest:
=ps;
{如果当前价值比当前最优答案优,则修改当前最优答案}
ifps+(s[n]-s[k-1])<
bestthenexit;
{剪枝}
=v[k]thensearch(k+1,restv-v[k],ps+p[k]);
{取该物品}
search(k+1,restv,ps);
{不取该物品}
begin{main}
=1tondoread(p[i]);
=s[i-1]+p[i];
search(1,maxv,0);
1003
704020
504030
80
54321
14
方法2:
根据第n件物品取与不取的两种情况,可以得到动态转移方程为:
f[n,maxv]=max{f[n-1,maxv-v[n]],f[n-1,maxv]}。
也可以降为一维:
f[j]=max{f[j-v[i]]+p[i],f[j]},j从maxv到v[i],i从1到n。
Programex2d;
vari,j,best,n,maxv:
v,p:
array[0..20000]ofinteger;
fillchar(f,sizeof(f),0);
iff[j-v[i]]+p[i]>
f[j]thenf[j]:
=f[j-v[i]]+p[i];
iff[j]>
=f[j];
3、求恰好装满的情况数
转移方程为:
f[n,v]=f[n-1,v-w[n]]+f[n-1,v],表示从n件物品中取若干件,组成的体积为v的情况数,f[0,0]=1。
Programex2e;
array[0..maxn,0..20000]ofinteger;
f[0,0]:
=1;
=0tomaxvdo
ifj=0thenf[i,j]:
=1
elseifj>
=v[i]thenf[i,j]:
=f[i-1,j-v[i]]+f[i-1,j]
elsef[i,j]:
=f[i-1,j];
writeln(f[n,maxv]);
3
二、0-1无限背包(完全背包)
例3、采药改编
把题目的每株草药都改成:
每种草药的数量都足够多
样例的输出为:
140
问题就变成了0/1无限背包问题,可以用动态规划做,问题就是求采摘第m种草药需要的时间所能达到的的最大价值。
设方程f(j)表示取第i种草药(i为变量),花去时间j所能达到的最大价值,则动态转移方程为:
f(j)=max(f(j),f(j-time[i])+p[i])。
Programex3;
vartime:
array[1..100]oflongint;
p:
f:
array[0..1000]oflongint;
i,ans,t,m,j:
longint;
functionmax(a,b:
longint):
ifa>
bthenmax:
=aelsemax:
=b;
readln(t,m);
=1tomdoreadln(time[i],p[i]);
=0totdof[i]:
=1tomdo
forj:
=time[i]totdo
=max(f[j],f[j-time[i]]+p[i]);
ans:
=1totdoans:
=max(ans,f[i]);
writeln(ans);
三、0-1有限背包的变形——多重背包
例4、多重背包
[问题题目]
有N种物品和一个容量为V的背包。
第i种物品最多有m[i]件可用,每件体积是v[i],价值是w[i]。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
504030{p1,p2,……,pn}
123{m1,m2,m3……,mn}
本题和0-1无限背包问题很类似。
基本的方程只需将0-1无限背包问题的方程略微一改即可,因为对于第i种物品有m[i]+1种策略:
取0件,取1件……取m[i]件。
令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<
=k<
=m[i]}。
复杂度是O(V*∑m[i])。
另一种好想好写的基本方法是转化为0-1有限背包求解:
把第i种物品换成m[i]件0-1有限背包中的物品,则得到了物品数为∑m[i]的01有限背包问题,直接求解,复杂度仍然是O(V*∑m[i])。
但是我们期望将它转化为01有限背包问题之后能够像0-1无限背包一样降低复杂度。
应用二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..m[i]件——均能等价于取若干件代换以后的物品。
另外,取超过m[i]件的策略必不能出现。
方法是:
将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。
使这些系数分别为1,2,4,...,2^(k-1),m[i]-2^k+1,且k是满足m[i]-2^k+1>
0的最大整数。
例如,如果m[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
分成的这几件物品的系数和为m[i],表明不可能取多于m[i]件的第i种物品。
另外这种方法也能保证对于0..m[i]间的每一个整数,均可以用若干个系数的和表示。
这样就将第i种物品分成了(logm[i])种物品,将原问题转化为了复杂度为O(V*∑logm[i])的0-1有限背包问题,是很大的改进。
Programex4;
vari,j,best,n,maxv,k,t:
v,p,num,v1,p1:
=1tondoread(num[i]);
k:
{组合后的物品个数}
=1tondo{对第i件物品进行组合}
t:
whilenum[i]>
0do
ifnum[i]>
=tthenbegin
inc(k);
v1[k]:
=v[i]*t;
p1[k]:
=p[i]*t;
num[i]:
=num[i]-t;
=t*2;
end
elsebegin
=v[i]*num[i];
=p[i]*num[i];
=v1;
=p1;
n:
=k;
{更新物品}
=1tondo{01背包}
例5、求自然数n(<
=1000)本质上不同的质数和的表达式的数目。
17
31
111
47
614
101
43709
穷举
生成每一个质数的系数的排列,再一一测试。
效率显然很低。
Programex5a;
varn,l,now,tot,i,j:
xs:
array[0..1001]oflongint;
pr:
flag:
boolean;
procedurecal(x:
longint);
vari:
now:
=1toxdo
inc(now,xs[i]*pr[i]);
proceduretry(dep:
vari,j:
cal(dep-1);
ifnow>
nthenexit;
ifdep=l+1then
ifnow=ntheninc(tot);
exit;
=0tondivpr[dep]do
xs[dep]:
=i;
try(dep+1);
readln(n);
l:
=2tondo
flag:
=2totrunc(sqrt(i))do
ifimodj=0then
ifflagthenbegininc(l);
pr[l]:
tot:
try
(1);
writeln(tot);
递归搜索
Programex5b;
varn,l,tot,i,j:
proceduretry(dep,rest:
vari,j,x:
if(rest<
=0)or(dep=l+1)then
ifrest=0theninc(tot);
=0torestdivpr[dep]do
try(dep+1,rest-pr[dep]*i);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 背包 问题