首页技术背包问题回溯法(回溯法-背包问题)

背包问题回溯法(回溯法-背包问题)

编程之家2024-01-23182次浏览

一、分别用回溯法和动态规划求0/1背包问题(C语言代码)

#include<stdio.h>

背包问题回溯法(回溯法-背包问题)

#include<malloc.h>

#include<windows.h>typedef struct goods

{

double*value;//价值

double*weight;//重量

char*select;//是否选中到方案

背包问题回溯法(回溯法-背包问题)

int num;//物品数量

double limitw;//限制重量

}GOODS;

double maxvalue,totalvalue;//方案最大价值,物品总价值

char*select1;//临时数组

void backpack(GOODS*g, int i, double tw, double tv)//参数为物品i,当前选择已经达到的重量和tw,本方案可能达到的总价值

背包问题回溯法(回溯法-背包问题)

{

int k;

if(tw+ g->weight[i]<= g->limitw)//将物品i包含在当前方案,且重量小于等于限制重量

{

select1[i]= 1;//选中第i个物品

if(i< g->num- 1)//若物品i不是最后一个物品

backpack(g, i+ 1, tw+ g->weight[i], tv);//递归调用,继续添加下一物品

else//若已到最后一个物品

{

for(k= 0; k< g->num;++k)//将状态标志复制到option数组中

g->select[k]= select1[k];

maxvalue= tv;//保存当前方案的最大价值

}

}

select1[i]= 0;//取消物品i的选择状态

if(tv- g->value[i]> maxvalue)//若物品总价值减去物品i的价值还大于maxv方案中已有的价值,说明还可以继续向方案中添加物品

{

if(i< g->num- 1)//若物品i不是最后一个物品

backpack(g, i+ 1, tw, tv- g->value[i]);//递归调用,继续加入下一物品

else//若已到最后一个物品

{

for(k= 0; k< g->num;++k)//将状态标志复制到option数组中

g->select[k]= select1[k];

maxvalue= tv- g->value[i];//保存当前方案的最大价值(从物品总价值中减去物品i的价值)

}

}

}

int main()

{

double sumweight;

GOODS g;

int i;

printf("背包最大重量:");

scanf("%lf",&g.limitw);

printf("可选物品数量:");

scanf("%d",&g.num);

if(!(g.value=(double*)malloc(sizeof(double)*g.num)))//分配内存保存物品价值

{

printf("内存分配失败\n");

exit(0);

}

if(!(g.weight=(double*)malloc(sizeof(double)*g.num)))//分配内存保存物品的重量

{

printf("内存分配失败\n");

exit(0);

}

if(!(g.select=(char*)malloc(sizeof(char)*g.num)))//分配内存保存物品的重量

{

printf("内存分配失败\n");

exit(0);

}

if(!(select1=(char*)malloc(sizeof(char)*g.num)))//分配内存保存物品的重量

{

printf("内存分配失败\n");

exit(0);

}

totalvalue=0;

for(i= 0; i< g.num; i++)

{

printf("输入第%d号物品的重量和价值:",i+ 1);

scanf("%lf%lf",&g.weight[i],&g.value[i]);

totalvalue+=g.value[i];//统计所有物品的价值总和

}

printf("\n背包最大能装的重量为:%.2f\n\n",g.limitw);

for(i= 0; i< g.num; i++)

printf("第%d号物品重:%.2f,价值:%.2f\n", i+ 1, g.weight[i], g.value[i]);

for(i= 0; i< g.num; i++)//初始设各物品都没加入选择集

select1[i]=0;

maxvalue=0;//加入方案物品的总价值

backpack(&g,0,0.0,totalvalue);//第0号物品加入方案,总重量为0,所有物品价值为totalvalue

sumweight=0;

printf("\n可将以下物品装入背包,使背包装的物品价值最大:\n");

for(i= 0; i< g.num;++i)

if(g.select[i])

{

printf("第%d号物品,重量:%.2f,价值:%.2f\n", i+ 1, g.weight[i], g.value[i]);

sumweight+=g.weight[i];

}

printf("\n总重量为:%.2f,总价值为:%.2f\n", sumweight, maxvalue);

// getch();

return 0;

}

二、背包问题的求解

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(X.*W2);disp('总的价值是:');disp(P*X');

时间复杂度是非指数的

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[0..maxn] 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较大时,容易超时.

4.2改进的递归法

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

一维数组即可

程序如下:

program knapsack04;

const maxm=2000;maxn=30;

type ar=array[0..maxn] of integer;

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

c,w:ar;

p:array[0..maxm] 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<stdlib.h>

#i nclude<conio.h>

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("\nSorry.We have not got a solution.\n");

}

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

#define K 10

#define N 10

#i nclude<stdlib.h>

#i nclude<conio.h>

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("\nSorry.We 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("\nSorry.We have not got a solution.\n");

}

4)动态规划算法

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

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

0/1背包问题

在0/ 1背包问题中,需对容量为c的背包进行装载。从n个物品中选取装入背包的物品,每件物品i的重量为wi,价值为pi。对于可行的背包装载,背包中物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,即p1*x1+p2*x1+...+pi*xi(其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= 1.n)值。

在该例中,可得出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。

三、用回溯法求01背包问题,怎样使用C++模板啊,迫切求指点!

#include<iostream>

using namespace std;

class Knap

{

friend int Knapsack(int p[],int w[],int c,int n);

public:

//输出当前最优解

void print()

{

for(int m=1;m<=n;m++)

{

cout<<bestx[m]<<"";

}

cout<<endl;

};

private:

int Bound(int i);//计算右子树的上界

void Backtrack(int i);

int c;//背包容量

int n;//物品数

int*w;//物品重量数组

int*p;//物品价值数组

int cw;//当前重量

int cp;//当前价值

int bestp;//当前最优值

int*bestx;//当前最优解

int*x;//当前解

};

int Knap::Bound(int i)

{

//计算上界

int cleft=c-cw;//剩余容量

int b=cp;

//以物品单位重量价值递减序装入物品

while(i<=n&&w[i]<=cleft)

{

cleft-=w[i];

b+=p[i];

i++;

}

//剩余物品取部分来装满背包

if(i<=n)

b+=p[i]/w[i]*cleft;

return b;

}

void Knap::Backtrack(int i)

{

if(i>n)//到达叶结点

{

if(bestp<cp)

{

for(int j=1;j<=n;j++)

bestx[j]=x[j];//更新当前最优解

bestp=cp;

}

return;

}

if(cw+w[i]<=c)//搜索左子树

{

x[i]=1;//装入

cw+=w[i];

cp+=p[i];

Backtrack(i+1);

cw-=w[i];

cp-=p[i];

}

if(Bound(i+1)>bestp)//搜索右子树

{

x[i]=0;//不装入

Backtrack(i+1);

}

}

class Object

{

friend int Knapsack(int p[],int w[],int c,int n);

public:

int operator<=(Object a)const

{

return(d>=a.d);

}

private:

int ID;

float d;//单位重量价值

};

int Knapsack(int p[],int w[],int c,int n)

{

//为Knap::Backtrack初始化

int W=0;

int P=0;

int i=1;

Object*Q=new Object[n];

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

{

Q[i-1].ID=i;

Q[i-1].d=1.0*p[i]/w[i];//计算单位重量价值

P+=p[i];

W+=w[i];

}

if(W<=c)

return P;//装入所有物品

//依物品单位重量排序

float f;

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

for(int j=i;j<n;j++)

{

if(Q[i].d<Q[j].d)

{

f=Q[i].d;

Q[i].d=Q[j].d;

Q[j].d=f;

}

}

Knap K;

K.p= new int[n+1];

K.w= new int[n+1];

K.x= new int[n+1];

K.bestx= new int[n+1];

K.x[0]=0;

K.bestx[0]=0;

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

{

K.p[i]=p[Q[i-1].ID];

K.w[i]=w[Q[i-1].ID];

}

K.cp=0;

K.cw=0;

K.c=c;

K.n=n;

K.bestp=0;

//回溯搜索

K.Backtrack(1);

K.print();

delete [] Q;

delete [] K.w;

delete [] K.p;

return K.bestp;

}

void main()

{

int*p;

int*w;

int c=7;

int n=4;

int i=0;

p=new int[n+1];

w=new int[n+1];

p[0]=0;

w[0]=0;

p[1]=9;w[1]=3;

p[2]=10;w[2]=5;

p[3]=7;w[3]=2;

p[4]=4;w[4]=1;

cout<<Knapsack(p,w,c,n)<<endl;

system("pause");

}

中星9号最新参数(广播卫星参数)网通服务器ip(网通服务器ip地址怎么设置)