算法设计: 五、回溯法(1. 0-1 背包问题)—— C++实现 - 算法分析

回朔法

回朔法有通用解题法之称,可以系统地搜索一个问题的所有解或者任一解,
他是一个即带有系统性又带有跳跃性的搜索算法。

回朔法算法解题的一般思路:

  1. 针对所给问题,定义问题的解空间;
  2. 确定易于搜索的解空间结构;
  3. 以深度优先的方式搜索解空间。

利用回朔法求解 0-1 背包问题。

我们有 n 种物品,物品 j 的重量为 wj,价格为 pj。我们假定所有物品的重
量和价格都是非负的。背包所能承受的最大重量为 c。如果限定每种物品只能选
择 0 个或 1 个。
有六件物品,体积和价值见下表,背包容量为 25

i(物品)123456
w(体积)117912310
v(价值)10579212

求解思想:

(1)定义问题解空间;
(2)确定易于搜索的解空间结构,该问题的解空间结构为子集树;
在这里插入图片描述
(3)采用深度优先方式搜索解空间,同时要及时剪枝,避免无效搜索。该问
题的限界函数为:
在这里插入图片描述

数据结构和变量:

typedef struct {
	float		w;			/* 物体重量 */
	float		p;			/* 物体价值 */
	float		v;			/* 物体的价值重量比 */
} OBJECT;

OBJECT	ob[n];			
float		M;				/* 背包载重量 */
int		x[n];			/* 可能的解向量 */
int		y[n];			/* 当前搜索的解向量 */
float		p_est;			/* 当前搜索方向装入背包物体的估计最大价值 */
float		p_total;		/* 装入背包的物体的最大价值的上界 */
float		w_cur;			/* 当前装入背包的物体的总重量 */
float		p_cur;			/* 当前装入背包的物体的总价值 */

0/1背包问题的回溯算法:

 1. float knapsack_back(OBJECT ob[],float M,int n,BOOL x[])
 2. {
 3.     int i,k;
 4.     float w_cur,p_total,p_cur,w_est,p_est;
 5.     BOOL *y = new BOOL[n]; 
 6.     for (i=0;i<=n;i++) {				/* 计算物体的价值重量比 */
 7.         ob[i].v = ob[i].p / ob[i].w;
 8.         y[i] = FALSE;					/* 当前的解向量初始化 */
 9.     }
10.     merge_sort(ob,n);					/* 物体按价值重量比的非增顺序排序*/
11.     w_cur = p_cur = p_total = 0;		/* 当前背包中物体的价值重量初始化*/
12.     k = 0;								/* 已搜索到的可能解的总价值初始化*/
13.     while (k>=0) {
14.         w_est = w_cur;   p_est = p_cur;
15.         for (i=k;i<n;i++) {			/* 沿当前分支可能取得的最大价值 */
16.             w_est = w_est + ob[i].w;
17.             if (w_est<M) {
18.                 p_est = p_est + ob[i].p;
19.             else {
20.                 p_est = p_est + ((M – w_est + ob[i].w) / ob[i].w) * ob[i].p;
21.                 break;
22.             }
23.         }
24.         if (p_est>p_total) {					/* 估计值大于上界 */
25.             for (i=k;i<n;i++) {
26.                 if (w_cur+ob[i].w<=M) {			/* 可装入第i个物体 */
27.                     w_cur = w_cur + ob[i].w; 
28.                     p_cur = p_cur + ob[i].p;
29.                     y[i] = TRUE;  
30.                 } 
31.                 else {
32.                     y[i] = FALSE;   break;		/* 不能装入第i个物体 */
33.                 }
34.             }
35.             if (i>=n) {							/* n个物体已全部装入 */
36.                 if (p_cur>p_total) {
37.                     p_total = p_cur;   k = n;	/* 刷新当前上限 */
38.                     for (i=0;i<n;i++) 			/* 保存可能的解 */
39.                         x[i] = y[i];			
40.                 }    
41.             }
42.             else k = i + 1;					/* 继续装入其余物体 */
43.         }
44.         else {								/* 估计价值小于当前上限*/
45.             while ((i>=0)&&(y[i]==0)		/* 沿着右分支结点方向回溯*/
46.                 i = i – 1;					/* 直到左分支结点 */
47.             if (i<0) break;					/* 已到达根结点,算法结束 */
48.             else {
49.                 w_cur = w_cur – ob[i].w;		/* 修改当前值 */	
50.                 p_cur = p_cur – ob[i].p;
51.                 y[i] = FALSE;   k = i + 1;	/* 搜索右分支子树 */
52.             }
53.         }
54.     }
55.     delete y;
56.     return p_total;
57. }

算法分析

算法在最坏情况下所花费的时间: O(n·2n)
合并排序,需花费 O(n·log n) 时间;
在最坏情况下,状态空间树有 2n+1-1个结点,有 O(2n) 儿子结点,
每个右儿子结点都需估计继续搜索可能取得的目标函数的最大价值,每次估计时间需花费 O(n) 时间,因此,右儿子结点需花费 O(n·2n) 时间

工作空间为:O(n)

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值