背包问题的求解

背包问题的求解,第1张

1)登山算法

用登山算法求解背包问题 function []=DengShan(n,G,P,W) %n是背包的个数,G是背包的总容量,P是价值向量,W是物体的重量向量 %n=3;G=20;P=[25,24,15];W2=[18,15,10];%输入量 W2=W; [Y,I]=sort(-P/W2);W1=[];X=[];X1=[]; for i=1:length(I) W1(i)=W2(I(i)); end W=W1; for i=1:n X(i)=0; RES=G;%背包的剩余容量 j=1; while W(j)<=RES X(j)=1; RES=RES-W(j); j=j+1; end X(j)=RES/W(j); end for i=1:length(I) X1(I(i))=X(i); end X=X1; disp('装包的方法是');disp(X);disp(XW2);disp('总的价值是:');disp(PX');

时间复杂度是非指数的

2)递归法

先看完全背包问题

一个旅行者有一个最多能用m公斤的背包,现在有n种物品,每件的重量分别是W1,W2,,Wn,

每件的价值分别为C1,C2,,Cn若的每种物品的件数足够多

求旅行者能获得的最大总价值。

本问题的数学模型如下:

设 f(x)表示重量不超过x公斤的最大价值,

则 f(x)=max{f(x-i)+c[i]} 当x>=w[i] 1<=i<=n

可使用递归法解决问题程序如下:

program knapsack04;

const maxm=200;maxn=30;

type ar=array[0maxn] of integer;

var m,n,j,i,t:integer;

c,w:ar;

function f(x:integer):integer;

var i,t,m:integer;

begin

if x=0 then f:=0 else

begin

t:=-1;

for i:=1 to n do

begin

if x>=w[i] then m:=f(x-i)+c[i];

if m>t then t:=m;

end;

f:=t;

end;

end;

begin

readln(m,n);

for i:= 1 to n do

readln(w[i],c[i]);

writeln(f(m));

end

说明:当m不大时,编程很简单,但当m较大时,容易超时

42 改进的递归法

改进的的递归法的思想还是以空间换时间,这只要将递归函数计算过程中的各个子函数的值保存起来,开辟一个

一维数组即可

程序如下:

program knapsack04;

const maxm=2000;maxn=30;

type ar=array[0maxn] of integer;

var m,n,j,i,t:integer;

c,w:ar;

p:array[0maxm] of integer;

function f(x:integer):integer;

var i,t,m:integer;

begin

if p[x]<>-1 then f:=p[x]

else

begin

if x=0 then p[x]:=0 else

begin

t:=-1;

for i:=1 to n do

begin

if x>=w[i] then m:=f(i-w[i])+c[i];

if m>t then t:=m;

end;

p[x]:=t;

end;

f:=p[x];

end;

end;

begin

readln(m,n);

for i:= 1 to n do

readln(w[i],c[i]);

fillchar(p,sizeof(p),-1);

writeln(f(m));

end

3)贪婪算法

改进的背包问题:给定一个超递增序列和一个背包的容量,然后在超递增序列中选(只能选一次)或不选每一个数值,使得选中的数值的和正好等于背包的容量。

代码思路:从最大的元素开始遍历超递增序列中的每个元素,若背包还有大于或等于当前元素值的空间,则放入,然后继续判断下一个元素;若背包剩余空间小于当前元素值,则判断下一个元素

简单模拟如下:

#define K 10

#define N 10

#i nclude <stdlibh>

#i nclude <conioh>

void create(long array[],int n,int k)

{/产生超递增序列/

int i,j;

array[0]=1;

for(i=1;i<n;i++)

{

long t=0;

for(j=0;j<i;j++)

t=t+array[j];

array[i]=t+random(k)+1;

}

}

void output(long array[],int n)

{/输出当前的超递增序列/

int i;

for(i=0;i<n;i++)

{

if(i%5==0)

printf("\n");

printf("%14ld",array[i]);

}

}

void beibao(long array[],int cankao[],long value,int count)

{/背包问题求解/

int i;

long r=value;

for(i=count-1;i>=0;i--)/遍历超递增序列中的每个元素/

{

if(r>=array[i])/如果当前元素还可以放入背包,即背包剩余空间还大于当前元素/

{

r=r-array[i];

cankao[i]=1;

}

else/背包剩余空间小于当前元素值/

cankao[i]=0;

}

}

void main()

{

long array[N];

int cankao[N]={0};

int i;

long value,value1=0;

clrscr();

create(array,N,K);

output(array,N);

printf("\nInput the value of beibao:\n");

scanf("%ld",&value);

beibao(array,cankao,value,N);

for(i=0;i<N;i++)/所有已经选中的元素之和/

if(cankao[i]==1)

value1+=array[i];

if(value==value1)

{

printf("\nWe have got a solution,that is:\n");

for(i=0;i<N;i++)

if(cankao[i]==1)

{

if(i%5==0)

printf("\n");

printf("%13ld",array[i]);

}

}

else

printf("\nSorryWe have not got a solution\n");

}

贪婪算法的另一种写法,beibao函数是以前的代码,用来比较两种算法:

#define K 10

#define N 10

#i nclude <stdlibh>

#i nclude <conioh>

void create(long array[],int n,int k)

{

int i,j;

array[0]=1;

for(i=1;i<n;i++)

{

long t=0;

for(j=0;j<i;j++)

t=t+array[j];

array[i]=t+random(k)+1;

}

}

void output(long array[],int n)

{

int i;

for(i=0;i<n;i++)

{

if(i%5==0)

printf("\n");

printf("%14ld",array[i]);

}

}

void beibao(long array[],int cankao[],long value,int count)

{

int i;

long r=value;

for(i=count-1;i>=0;i--)

{

if(r>=array[i])

{

r=r-array[i];

cankao[i]=1;

}

else

cankao[i]=0;

}

}

int beibao1(long array[],int cankao[],long value,int n)

{/贪婪算法/

int i;

long value1=0;

for(i=n-1;i>=0;i--)/先放大的物体,再考虑小的物体/

if((value1+array[i])<=value)/如果当前物体可以放入/

{

cankao[i]=1;/1表示放入/

value1+=array[i];/背包剩余容量减少/

}

else

cankao[i]=0;

if(value1==value)

return 1;

return 0;

}

void main()

{

long array[N];

int cankao[N]={0};

int cankao1[N]={0};

int i;

long value,value1=0;

clrscr();

create(array,N,K);

output(array,N);

printf("\nInput the value of beibao:\n");

scanf("%ld",&value);

beibao(array,cankao,value,N);

for(i=0;i<N;i++)

if(cankao[i]==1)

value1+=array[i];

if(value==value1)

{

printf("\nWe have got a solution,that is:\n");

for(i=0;i<N;i++)

if(cankao[i]==1)

{

if(i%5==0)

printf("\n");

printf("%13ld",array[i]);

}

}

else

printf("\nSorryWe have not got a solution\n");

printf("\nSecond method:\n");

if(beibao1(array,cankao1,value,N)==1)

{

for(i=0;i<N;i++)

if(cankao1[i]==1)

{

if(i%5==0)

printf("\n");

printf("%13ld",array[i]);

}

}

else

printf("\nSorryWe have not got a solution\n");

}

4)动态规划算法

解决0/1背包问题的方法有多种,最常用的有贪婪法和动态规划法。其中贪婪法无法得到问题的最优解,而动态规划法都可以得到最优解,下面是用动态规划法来解决0/1背包问题。

动态规划算法与分治法类似,其基本思想是将待求解问题分解成若干个子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是互相独立的,若用分治法解这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费过多的时间。动态规划法又和贪婪算法有些一样,在动态规划中,可将一个问题的解决方案视为一系列决策的结果。不同的是,在贪婪算法中,每采用一次贪婪准则便做出一个不可撤回的决策,而在动态规划中,还要考察每个最优决策序列中是否包含一个最优子序列。

0/1背包问题

在0 / 1背包问题中,需对容量为c 的背包进行装载。从n 个物品中选取装入背包的物品,每件物品i 的重量为wi ,价值为pi 。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即p1x1+p2x1++pixi(其1<=i<=n,x取0或1,取1表示选取物品i) 取得最大值。

在该问题中需要决定x1 xn的值。假设按i = 1,2,,n 的次序来确定xi 的值。如果置x1 = 0,则问题转变为相对于其余物品(即物品2,3,,n),背包容量仍为c 的背包问题。若置x1 = 1,问题就变为关于最大背包容量为c-w1 的问题。现设r{c,c-w1 } 为剩余的背包容量。

在第一次决策之后,剩下的问题便是考虑背包容量为r 时的决策。不管x1 是0或是1,[x2 ,,xn ] 必须是第一次决策之后的一个最优方案,如果不是,则会有一个更好的方案[y2,,yn ],因而[x1,y2,,yn ]是一个更好的方案。

假设n=3, w=[100,14,10], p=[20,18,15], c= 116。若设x1 = 1,则在本次决策之后,可用的背包容量为r= 116-100=16 。[x2,x3 ]=[0,1] 符合容量限制的条件,所得值为1 5,但因为[x2,x3 ]= [1,0] 同样符合容量条件且所得值为1 8,因此[x2,x3 ] = [ 0,1] 并非最优策略。即x= [ 1,0,1] 可改进为x= [ 1,1,0 ]。若设x1 = 0,则对于剩下的两种物品而言,容量限制条件为116。总之,如果子问题的结果[x2,x3 ]不是剩余情况下的一个最优解,则[x1,x2,x3 ]也不会是总体的最优解。在此问题中,最优决策序列由最优决策子序列组成。假设f (i,y) 表示剩余容量为y,剩余物品为i,i + 1,,n 时的最优解的值,即:利用最优序列由最优子序列构成的结论,可得到f 的递归式为:

当j>=wi时: f(i,j)=max{f(i+1,j),f(i+1,j-wi)+vi} ①式

当0<=j<wi时:f(i,j)=f(i+1,j) ②式

fn( 1 ,c) 是初始时背包问题的最优解。

以本题为例:若0≤y<1 0,则f ( 3 ,y) = 0;若y≥1 0,f ( 3 ,y) = 1 5。利用②式,可得f (2, y) = 0 ( 0≤y<10 );f(2,y)= 1 5(1 0≤y<1 4);f(2,y)= 1 8(1 4≤y<2 4)和f(2,y)= 3 3(y≥2 4)。因此最优解f ( 1 , 11 6 ) = m a x {f(2,11 6),f(2,11 6 - w1)+ p1} = m a x {f(2,11 6),f(2,1 6)+ 2 0 } = m a x { 3 3,3 8 } = 3 8。

现在计算xi 值,步骤如下:若f ( 1 ,c) =f ( 2 ,c),则x1 = 0,否则x1 = 1。接下来需从剩余容量c-w1中寻求最优解,用f (2, c-w1) 表示最优解。依此类推,可得到所有的xi (i= 1n) 值。

在该例中,可得出f ( 2 , 116 ) = 3 3≠f ( 1 , 11 6 ),所以x1 = 1。接着利用返回值3 8 -p1=18 计算x2 及x3,此时r = 11 6 -w1 = 1 6,又由f ( 2 , 1 6 ) = 1 8,得f ( 3 , 1 6 ) = 1 4≠f ( 2 , 1 6 ),因此x2 = 1,此时r= 1 6 -w2 = 2,所以f (3,2) =0,即得x3 = 0。

贪婪法是一种不追求最优解,只希望得到较为满意解的方法。贪婪法一般可以快速得到满意的解,因为它省去了为找最优解要穷尽所有可能而必须耗费的大量时间。贪婪法常以当前情况为基础作最优选择,而不考虑各种可能的整体情况,所以贪婪法不要回溯。

贪婪算法的一般方法

1、问题描述

它有n个输入,而它的解就由这n个输入的某个子集组成,只是这个子集必须满足某些事先给定的条件。

2、约束条件 那些必须满足的条件称为约束条件。

3、可行解 满足约束条件的子集称为该问题的可行解。

4、目标函数 事先给定的衡量可行解优劣的量度标准,通常以函数的形式给出,称为目标函数。

5、最优解 使目标函数取极值(极大或极小)的可行解,称为最优解。

6、子结构模式 贪心技术中,问题的最优一般是原输入的子集,获取最优子集的贪心方法为子结构模式

7、有序模式 通过计算已有的判定而得出的最优条件,可以为下一步的判定提供依据,这种形式的贪心算法称为有序模式。

8、贪婪算法求解思想(分步处理)

�8�4 根据题意,选取一种量度标准;

�8�4 然后按这种量度标准对这n个输入排序,并按序一次输入一个量。

�8�4 如果这个输入和当前已构成在这种量度意义下的部分最优解加在一起不能产生一个可行解,则不把此输入加到这部分解中。

这种能够得到某种意义下的最优解的分级处理方法称为贪心算法。

上面的 思路不错

最优服务次序问题

一、问题描述:

设有n 个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≦i ≦n 。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小平均等待时间是n 个顾客等待服务时间的总和除以n。

二、贪心选择策略

假设原问题为T,而我们已经知道了某个最优服务系列,即最优解为A={t(1),t(2),….t(n)}(其中t(i)为第i个用户需要的服务时间),则每个用户等待时间为:

T(1)=t(1);T(2)=t(1)+t(2);T(n):t(1)+t(2)+t(3)+……t(n);

那么总等待时问,即最优值为:

TA=nt(1)+(n-1)t(2)+…+(n+1-j)t(i)+…2t(n-1)+t(n);

由于平均等待时间是n个顾客等待时间的总和除以n,故本题实际上就是求使顾客等待时间的总和最小的服务次序。

本问题采用贪心算法求解,贪心策略如下:对服务时间最短的顾客先服务的贪心选择策略。首先对需要服务时间最短的顾客进行服务,即做完第一次选择后,原问题T变成了需对n-1个顾客服务的新问题T’。新问题和原问题相同,只是问题规模由n减小为n-1。基于此种选择策略,对新问题T’,选择n-1顾客中选择服务时间最短的先进行服务,如此进行下去,直至所有服务都完成为止 。

三、问题的贪心选择性质

先来证明该问题具有贪心选择性质,即最优服务A中t(1)满足条件:t(1)<=t(i)(2<i<n)。

用反证法来证明:假设t(1)不是最小的,不妨设t(1)>t(i)(i>1)。

设另一服务序列B=(t(i),t(2),…,t(1)…,t(n))

那么TA-TB=n[t(1)-t(i)]+(n+1-i)[t(i)-t(1)]=(1-i)[t(i)-t(1)]>0

即TA>TB,这与A是最优服务相矛盾。

故最优服务次序问题满足贪心选择性质。

四、问题的最优子结构性质

在进行了贪心选择后,原问题T就变成了如何安排剩余的n-1个顾客的服务次序的问题T’,是原问题的子问题。

若A是原问题T的最优解,则A’={t(2),…t(i)…t(n))是服务次序问题子问题T’的最优解。

证明:假设A’不是子问题T’的最优解,其子问题的最优解为B’,则有TB’<TA’,而根据TA的定义知,TA’十t(1)=TA。因此TB’+t(1)<TA’+t(1)=TA,即存在一个比最优值TA更短的总等待时间,而这与TA为问题T的最优值相矛盾。因此,A’是子问题T’的最优值。

从以上贪心选择及最优子结构性质的证明,可知对最优服务次序问题用贪心算法可求得最优解。

根据以上证明,最优服务次序问题可以用最短服务时间优先的贪心选择可以达到最优解。故只需对所有服务先按服务时间从小到大进行排序,然后按照排序结果依次进行服务即可。平均等待时间即为TA/n。

五、算法实现

由多处最优服务次序问题具有贪心选择性质和最优子结构性质,容易证明算法greedy的正确性。本算法采用最短服务时间优先的贪心策略。首先将每个顾客所需要的服务时间从小到大排序。然后申请2个数组:st[]是服务数组,st[j]为第j个队列上的某一个顾客的等待时间;su[]是求和数组,su[j]的值为第j个队列上所有顾客的等待时间;

double greedy(vector<int>x,int s)

{

vector<int>st(s+1,0);

vector<int>su(s+1,0);

int n=xsize();

sort(xbegin(),xend());

int i=0,j=0;

while(i<n){

st[j]+=x[i];

su[j]+=st[j];

i++;

j++;

if(j==s)j=0;//循环分配顾客到每一个服务点上

}

double t=0;

for(i=0;i<s;i++) t+=su[i];

t/=n;

return t;

}

六、算法测试结果

七、算法复杂性分析

程序主要是花费在对各顾客所需服务时间的排序和贪心算法,即计算平均服务时间上面。其中,贪心算法部分只有一重循环影响时间复杂度,其时间复杂度为O(n):而排序算法的时间复杂度为O(nlogn)。因此,综合来看算法的时间复杂度为O(nlogn)。

八、参考文献

[1] 王晓东计算机算法设计与分析(第3版)[M].北京:电子工业出版社,2007.

[2] 陈媛《算法与数据结构》[M],清华大学出版社, 第1版,20054

[3] 王晓东算法设计与实验题解 [M].北京:电子工业出版社,2008.

附录(程序代码)

#include<iostream>

#include <vector>

#include<algorithm>

using namespace std;

using std::vector;

double greedy(vector<int>x,int s)

{

vector<int>st(s+1,0);

vector<int>su(s+1,0);

int n=xsize();

sort(xbegin(),xend());

int i=0,j=0;

while(i<n){

st[j]+=x[i];

su[j]+=st[j];

i++;

j++;

if(j==s)j=0;

}

double t=0;

for(i=0;i<s;i++) t+=su[i];

t/=n;

return t;

}

void main()

{int n;//等待服务的顾客人数

int s;//服务点的个数

int i;

int a;

int t;//平均服务时间

vector<int>x;

cout<<"please input the num of the customer:"<<endl;

cin>>n;

cout<<"please input the num of the server:"<<endl;

cin>>s;

cout<<"please input the need service time of each customer:"<<endl;

for(i=1;i<=n;i++){

cout<<"No"<<i<<endl;

cin>>a;

xpush_back(a);

}

t=greedy(x, s);

cout<<"the least average waiting time is:"<<t<<endl;

}

算法应该具有以下五个重要的特征:

1,有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止;

2,确切性:算法的每一步骤必须有确切的定义;

3,输入项:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;

4,输出项:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;

5,可行性:算法中执行的任何计算步骤都是可以被分解为基本的可执行的 *** 作步,即每个计算步都可以在有限时间内完成(也称之为有效性)。

扩展资料:

对于一个给定的问题,往往可能有好几种量度标准。初看起来,这些量度标准似乎都是可取的,但实际上,用其中的大多数量度标准作贪婪处理所得到该量度意义下的最优解并不是问题的最优解,而是次优解。因此,选择能产生问题最优解的最优量度标准是使用贪婪算法的核心。

一般情况下,要选出最优量度标准并不是一件容易的事,但对某问题能选择出最优量度标准后,用贪婪算法求解则特别有效。

若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

以上就是关于背包问题的求解全部的内容,包括:背包问题的求解、程序设计一课中提到的贪婪法基本思想是什么啊、5. 设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti,1<=i<=n。应如何安排n个顾客的服务次序才能等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zz/9764651.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-01
下一篇2023-05-01

发表评论

登录后才能评论

评论列表(0条)

    保存