回溯法解决0-1背包问题

时间:2022-08-03 18:42:19

问题描述:

  有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。
回溯法

  01背包属于找最优解问题,用回溯法需要构造解的子集树。在搜索状态空间树时,只要左子节点是可一个可行结点,搜索就进入其左子树。对于右子树时,先计算上界函数,以判断是否将其减去,剪枝啦啦!
   上界函数bound():当前价值cw+剩余容量可容纳的最大价值<=当前最优价值bestp。
   为了更好地计算和运用上界函数剪枝,选择先将物品按照其单位重量价值从大到小排序,此后就按照顺序考虑各个物品。

 

#include <stdio.h>
#include
<conio.h>

int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值
double w[100];//各个物品的重量
double cw = 0.0;//当前背包重量
double cp = 0.0;//当前背包中物品价值
double bestp = 0.0;//当前最优价值
double perp[100];//单位物品价值排序后
int order[100];//物品编号
int put[100];//设置是否装入

//按单位价值排序
void knapsack()
{
int i,j;
int temporder = 0;
double temp = 0.0;

for(i=1;i<=n;i++)
perp[i]
=v[i]/w[i];
for(i=1;i<=n-1;i++)
{
for(j=i+1;j<=n;j++)
if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]
{
temp
= perp[i];
perp[i]
=perp[i];
perp[j]
=temp;

temporder
=order[i];
order[i]
=order[j];
order[j]
=temporder;
temp
= v[i];
v[i]
=v[j];
v[j]
=temp;

temp
=w[i];
w[i]
=w[j];
w[j]
=temp;
}
}
}

//回溯函数
void backtrack(int i)
{
double bound(int i);
if(i>n)
{
bestp
= cp;
return;
}
if(cw+w[i]<=c)
{
cw
+=w[i];
cp
+=v[i];
put[i]
=1;
backtrack(i
+1);
cw
-=w[i];
cp
-=v[i];
}
if(bound(i+1)>bestp)//符合条件搜索右子数
backtrack(i+1);
}

//计算上界函数
double bound(int i)
{
double leftw= c-cw;
double b = cp;
while(i<=n&&w[i]<=leftw)
{
leftw
-=w[i];
b
+=v[i];
i
++;
}
if(i<=n)
b
+=v[i]/w[i]*leftw;
return b;

}


int main()
{
int i;
printf(
"请输入物品的数量和容量:");
scanf(
"%d %lf",&n,&c);
printf(
"请输入物品的重量和价值:");
for(i=1;i<=n;i++)
{
printf(
"第%d个物品的重量:",i);
scanf(
"%lf",&w[i]);
printf(
"价值是:");
scanf(
"%lf",&v[i]);
order[i]
=i;
}
knapsack();
backtrack(
1);
printf(
"最有价值为:%lf\n",bestp);
printf(
"需要装入的物品编号是:");
for(i=1;i<=n;i++)
{
if(put[i]==1)
printf(
"%d ",order[i]);
}
return 0;
}

 

时间复杂度分析:

  上界函数bound()需要O(n)时间,在最坏的情况下有O(2^n)个右子结点需要计算上界,回溯算法backtrack需要的计算时间为O(n2^n)