赞
踩
注:上述考虑的物品重量和背包容量都为整数。因此,使用二维数组m[i][j]表示m(i,j)
以下是源代码:
- #include "stdafx.h";
- #include <iostream>;
- using namespace std;
-
- #define C 10
- #define N 5
-
- void Knapsack(int *w, int *v, int n, int c, int (*m)[C+1])
- {
- //先处理记录表格中的最后一行,也即完成对m[n][j]的“填空”
- int lowc = w[n]-1 > c ? c : w[n]-1;
- for (int j = 0; j <= lowc; j++)
- m[n][j] = 0;
- for (int j = lowc+1; j <= c; j++)
- m[n][j] = v[n];
- //循环处理记录表格中的其他行,也即完成对m[i][j]的“填空”
- for (int i = n-1; i > 1; i--)
- {
- lowc = w[i]-1 > c ? c : w[i]-1;
- for (int j = 0; j <=lowc; j++)
- m[i][j] = m[i+1][j];
- for (int j = lowc+1; j <=c; j++)
- {
- int t1 = m[i+1][j];
- int t2 = m[i+1][j - w[i]] + v[i];
- m[i][j] = t1 > t2 ? t1 : t2;
- }
- }
- //处理记录表格中的第一行,也即完成对m[1][c]的“填空”
- m[1][c] = m[2][c]; //背包容量c不足,物品1舍去
- if(c >= w[1] && m[2][c-w[1]] + v[1] > m[2][c]) //背包容量c足够,并且选取物品1的情况价值较大
- m[1][c] = m[2][c-w[1]] +v[1];
- }
-
- void Traceback(int (*m)[C+1],int *w, int *x,int n, int c)
- {
- for (int i = 1; i < n; i++)
- {
- if(m[i][c] == m[i+1][c])
- x[i] = 0;
- else
- {
- x[i] = 1;
- c -= w[i];
- }
- }
- w[n] < c ? x[n] =1 : x[n] =0;
- }
-
- int _tmain()
- {
- int x[N+1];
- int w[N+1] ={-1,2,2,6,5,4};
- int v[N+1] ={-1,6,3,5,4,6};
- int m[N+1][C+1];
- Knapsack(w,v,N,C,m);
- cout<<m[1][C]<<endl;
- Traceback(m, w, x, N, C);
- for (int i = 1; i <= N; i++)
- {
- cout<<x[i]<<" ";
- }
- cout<<endl;
- system("pause");
- return 0;
- }

运行结果如下:
15
1 1 0 0 1
从上述算法分析中,有两个较明显的缺点。其一是算法要求所给物品的重量wi是整数。其次,背包容量c很大时,算法所需要的计算时间和空间较多。因此,对上述算法提出改进,使物品重量和背包容量允许为实数(大于0的实数),同时减少所需的时间和空间复杂度。改进分析:
1.将二维表m(i,j)看成函数,对于确定的i,函数m(i,j)是关于变量j的阶梯状单调不减函数。通过跳跃点描述阶梯状的函数m(i,j)。比如,物品n重量大小5,价值为8那么,
当j>=5时,m(n,j) = 8 ;当0<= j < 5时, m(n,j) = 0;可以看出背包容量大小5是m(n,j)的分界容量,所以,(0,0)和(5,8)是函数m(n,j)的两个跳跃点。因此,只需对函数m(i,j)所有的跳跃点保存,无需建立二维表m,从而节省内存空间。最后的最优值从函数m(1,j)的所有跳跃点中获得。
2.每个跳跃点对应一个数组,记录以往选取物品的编号。最优解的构造从函数m(1,j)中的第一个不超过背包容量c对应的跳跃点中获得。(注意,对于确定的i,函数m(i,j)所有的跳跃点按j递增排序。)
3.用p[i+1]表示函数m(i+1,j)的跳跃点集,q[i+1]表示p[i+1]包含物品i之后的跳跃点集,则p[i]等于p[i+1]与q[i+1]的合并(并不是单纯合并,因为有些跳跃点受控于其他的跳跃点)。
举例说明:
背包容量c:5
物品数量n:3
物品重量数组w:2,3,5
物品价值数组v:6,4,8
初始化p[4]的跳跃点集:(0,0)
若加入物品3之后,出现的跳跃点:(5,8),所以合并之后,
p[3]的跳跃点集:(0,0),(5,8)
若加入物品2之后,出现的跳跃点:(3,4),(8,12),所以合并之后,
p[2]的跳跃点集:(0,0),(3,4),(5,8),(8,12)
若加入物品1之后,出现的跳跃点:(2,6),(5,10),(7,14),(10,18),所以合并之后,
p[1]的跳跃点集:(0,0),(2,6),(5,10),(7,14),(10,18)
最终的最优值从p[1]的跳跃点集中寻找,比如:
(1)若背包容量5,对应的跳跃点为(5,10),因此,背包装载物品的最大价值为10.
(2)若背包容量8,对应的跳跃点为(7,14),因此,背包装载物品的最大价值为14.
以下是改进算法:
- #include "stdafx.h"
- #include <iostream>
- using namespace std;
-
- //物品类
- class Object
- {
- friend class Knapsack;
- int number;
- double weight;
- double value;
- };
-
- //跳跃点类
- class JumpNode
- {
- friend class Knapsack;
- friend void Print();
- double enoughw; //排序的依据
- double enoughv;
- int amount;
- int *selected;
- JumpNode *next;
-
- };
-
- //0-1背包问题主类
- class Knapsack
- {
- Knapsack(int n, double *w, double *v, double c);
- friend void Print();
- public:
- void DynamicProgram();
- private:
- int n;
- double c;
- Object *Ot;
- JumpNode *firstp;
- JumpNode *firstq;
- JumpNode *abandon;
- };
-
- //动态规划解决0-1背包问题(物品重量允许大于零的实数)
- void Knapsack::DynamicProgram()
- {
- int i = n;
- for (; i >= 1; i--)
- {
- //对firstp中的结点用物品i进行“升级”,保存在firstq中
- JumpNode *jptr = firstp;
- JumpNode *lastq = firstq;
- bool isfq = true;
- while (jptr != nullptr)
- {
- if(jptr->enoughw + Ot[i].weight <= c)
- {
- JumpNode *temp;
- if(abandon != nullptr)
- {
- temp = abandon;
- abandon = temp->next;
- }
- else
- {
- temp = new JumpNode;
- temp->selected = new int[n+1];
- }
- temp->amount = jptr->amount + 1;
- temp->enoughw = jptr->enoughw + Ot[i].weight;
- temp->enoughv = jptr->enoughv + Ot[i].value;
- for (int j = 1; j < temp->amount ; j++)
- temp->selected[j] = jptr->selected[j];
- temp->selected[temp->amount] = Ot[i].number;
- temp->next = nullptr;
- //把temp结点插入到firstq中
- if(isfq)
- {
- firstq = temp;
- lastq = temp;
- isfq = false;
- }
- else
- {
- lastq->next = temp;
- lastq = temp;
- }
- }
- jptr = jptr->next;
- }
- if(!isfq)
- lastq->next = nullptr;
-
- //对firstp和firstq合并,最后放到firstp
- if(firstq != nullptr)
- {
- JumpNode *firsttemp = nullptr;
- JumpNode *temp = nullptr;
- bool isfirst = true;
- double cmaxvalue = 0;
- while ((firstp != nullptr) && (firstq != nullptr))
- {
- if(firstp->enoughw < firstq->enoughw)
- {
- if(isfirst)
- {
- temp = firstp;
- firstp = firstp->next;
- firsttemp = temp;
- isfirst = false;
- cmaxvalue = temp->enoughv;
- }
- else
- {
- temp->next = firstp;
- temp = firstp;
- firstp = firstp->next;
- cmaxvalue = temp->enoughv;
- }
- }
- else
- {
- if(firstp->enoughw == firstq->enoughw)
- {
- if(firstp->enoughv > firstq->enoughv)
- {
- temp->next = firstp;
- temp = firstp;
- firstp = firstp->next;
- cmaxvalue = temp->enoughv;
- JumpNode *ab;
- ab = firstq->next;
- if(abandon != nullptr) //回收
- {
- firstq->next = abandon->next;
- abandon->next = firstq;
- }
- else
- abandon = firstq;
- firstq = ab;
-
- }
- else
- {
- temp->next = firstq;
- temp = firstq;
- firstq = firstq->next;
- cmaxvalue = temp->enoughv;
- JumpNode *ab;
- ab = firstp->next;
- if(abandon != nullptr) //回收
- {
- firstp->next = abandon->next;
- abandon->next = firstp;
- }
- else
- abandon = firstp;
- firstp = ab;
- }
- }
- else //firstp->enoughw > firstq->enoughw
- {
- if(firstq->enoughv > cmaxvalue)
- {
- temp->next = firstq;
- temp = firstq;
- firstq = firstq->next;
- cmaxvalue = temp->enoughv;
- }
- else
- {
- JumpNode *ab;
- ab = firstq->next;
- if(abandon != nullptr) //回收
- {
- firstq->next = abandon->next;
- abandon->next = firstq;
- }
- else
- abandon = firstq;
- firstq = ab;
- }
- }
- }
-
- }//while
- if(firstq == nullptr)
- while (firstp != nullptr)
- {
- if(firstp->enoughv <= cmaxvalue) //回收
- {
- JumpNode *ab;
- ab = firstp->next;
- if(abandon != nullptr) //回收
- {
- firstp->next = abandon->next;
- abandon->next = firstp;
- }
- else
- abandon = firstp;
- firstp = ab;
- }
- else
- {
- temp->next = firstp;
- temp = firstp;
- firstp = firstp->next;
- cmaxvalue = temp->enoughv;
- }
- }
- else
- {
- while (firstq != nullptr)
- {
- if(firstq->enoughv <= cmaxvalue) //回收
- {
- JumpNode *ab;
- ab = firstq->next;
- if(abandon != nullptr) //回收
- {
- firstq->next = abandon->next;
- abandon->next = firstq;
- }
- else
- abandon = firstq;
- firstq = ab;
- }
- else
- {
- temp->next = firstq;
- temp = firstq;
- firstq = firstq->next;
- cmaxvalue = temp->enoughv;
- }
- }
- }
- temp->next = nullptr;
- firstp = firsttemp;
- }
- }//for
- }
-
- Knapsack::Knapsack(int n, double *w, double *v, double c):n(n),c(c)
- {
- Ot = new Object[n+1];
- for (int i = 1; i <=n; i++)
- {
- Ot[i].number = i;
- Ot[i].weight = w[i-1];
- Ot[i].value = v[i-1];
- }
- //创建跳跃点(0,0)
- JumpNode *newNode = new JumpNode;
- newNode->amount = 0;
- newNode->enoughv =0;
- newNode->enoughw =0;
- newNode->next =nullptr;
- newNode->selected = new int[n+1];
- firstp = newNode;
- firstq = nullptr;
- }
-
- void Print()
- {
- const int N = 4; //物品数量
- int c = 7; //背包容量
- double w[N] = {2,3,5,2}; //物品重量数组
- double v[N] = {6.6,4.6,8.5,4.6}; //物品价值数组
- Knapsack *K = new Knapsack(N, w, v, c);
- K->DynamicProgram();
- JumpNode *scan = K->firstp;
- int *selarr;
- int count = 0;
- double selvalue = 0;
- JumpNode *oldscan = scan;
- while (scan != nullptr)
- {
- if(scan->enoughw > c)
- break;
- oldscan = scan;
- scan = scan->next;
- }
- selarr = oldscan->selected;
- selvalue = oldscan->enoughv;
- count = oldscan->amount;
- if(count > 0)
- cout<<"已选的物品编号如下:"<<endl; //物品编号从1开始
- for (int i = count; i >0; i--)
- cout<<selarr[i]<<" ";
- if(count == 0)
- cout<<"背包容量不足,任何物品都不能装入背包"<<endl;
- else
- cout<<endl<<"背包装载的物品最大价值:"<<selvalue<<endl;
- system("pause");
- }
-
- int _tmain()
- {
- Print();
- return 0;
- }

运行结果如下:
已选的物品编号如下:
1 2 4
背包装载的物品最大价值:15.8
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。