完全背包问题

完全背包是在N物品中选取若干件(同一种物品可多次选取)放在空间为V的背包里,每物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解怎么装物品可使背包里物品总价值最大。

Fk(y):只装前 k 种物品,  总重不超过 y,  背包的最大价值
ik(y):只装前 k 种物品,  总重不超过 y,  背包达最大价值时装入物品的最大标号

递推方程和边界条件如下:
Fk(y)=max{Fk-1(y),Fk(y-wk)+vk}     max{不装第k种物品,至少一个k种物品}
F0(y)=0        0<=y<=b
Fk(0)=0        0<=k<=n
F1(y)=[y/w1]v1    只装第一种物品,总重量不超过y,[ ]向下取整
Fk(y)=-∞    y<0    设定边界值

标记函数ik(y)

ik(y)=ik-1(y)    Fk-1(y)>Fk(y-wk)+vk
ik(y)=k         Fk-1(y)<=Fk(y-wk)+vk

v1 = 1,   v2 = 3,  v3 = 5,  v4 = 9,
w1 = 2,   w2 = 3,  w3 = 4,  w4 = 7,
b = 10

只挑前1种物品的价值情况:
F1(0)=0
F1(1)=0      i1(1)=0
F1(2)=1      i1(2)=1
F1(3)=1      i1(3)=1
F1(4)=2      i1(4)=1
F1(5)=2      i1(5)=1
F1(6)=3      i1(6)=1
F1(7)=3      i1(7)=1
F1(8)=4      i1(8)=1
F1(9)=4         i1(9)=1
F1(10)=5     i1(10)=1


只挑前2种物品的价值情况:
F2(2-w2)+v2:  总重量为2-w2的情况下,只装前2种物品最大价值与第2种物品的价值和
F2(0)=0
F2(1)=max{F1(1),F2(1-w2)+v2}=max{0,-∞+3}=0                                        i2(1)=0

F2(2)=max{F1(2),F2(2-w2)+v2}=max{1,-∞+3}=1                            i2(2)=i1(2)=1

F2(3)=max{F1(3),F2(3-w2)+v2}=max{1,0+3}=3                          i2(3)=2

F2(4)=max{F1(4),F2(4-w2)+v2}=max{2,F2(4-3)+3}=max{2,F2(1)+3}=max{2,0+3}=3          i2(4)=2

F2(5)=max{F1(5),F2(5-w2)+v2}=max{2,F2(5-3)+3}=max{2,F2(2)+3}=max{2,1+3}=4    i2(5)=2

F2(6)=max{F1(6),F2(6-w2)+v2}=max{3,F2(6-3)+3}=max{3,F2(3)+3}=max{3,3+3}=6    i2(6)=2  (接着考虑i2(6-3))

F2(7)=max{F1(7),F2(7-w2)+v2}=max{3,F2(7-3)+3}=max{3,F2(4)+3}=max{3,3+3}=6    i2(7)=2     (接着考虑i2(7-3))

F2(8)=max{F1(8),F2(8-w2)+v2}=max{3,F2(8-3)+3}=max{3,F2(5)+3}=max{3,4+3}=7    i2(8)=2     (接着考虑i2(8-3))

F2(9)=max{F1(9),F2(9-w2)+v2}=max{3,F2(9-3)+3}=max{3,F2(6)+3}=max{3,6+3}=9    i2(9)=2     (接着考虑i2(9-3))
.......

F3(2)=max{F2(2),F3(2-w3)+v3}=max{1,F3(2-4)+5}=1                    i3(2)=1
...........

只挑前3种物品的价值情况:
F3(0)=0

F3(1)=max{F2(1),F3(1-w3)+v3}=max{0,-∞+5}=0                                        i3(1)=0

F3(2)=max{F2(2),F3(2-w3)+v3}=max{1,-∞+5}=1                                        i3(2)=i2(2)=i1(2)=1

F3(3)=max{F2(3),F3(3-w3)+v3}=max{3,-∞+5}=3                                        i3(3)=i2(3)=2

F3(4)=max{F2(4),F3(4-w3)+v3}=max{F2(4),F3(0)+v3}=max{3,0+5}=5               i3(4)=3

F3(5)=max{F2(5),F3(5-w3)+v3}=max{F2(5),F3(1)+v3}=max{4,0+5}=5               i3(5)=3

F3(6)=max{F2(6),F3(6-w3)+v3}=max{F2(6),F3(2)+v3}=max{6,1+5}=6               i3(6)=3   (把条件改成Fk-1(y)>=Fk(y-wk)+vk时 ik(y)=ik-1(y)   取最大物品的标号就会改变i3(6)=i2(6)=2  即只取前3种物品,总重量不超过6的情况下:会变成取两个2号物品)

.......................

一直要算到F4(10)的值           i4(10)


v1 = 2,   v2 = 4,  v3 = 2,  v4 = 1,  v5 = 10
w1 = 2,   w2 =12,  w3 = 1,  w4 = 1,  w5 = 4
b = 15


作业要求:
用户输入物品的数量,再输入每个物品价值与重量,再输入背包的总重量。(最好以数组直接定义的方式给出,免得输入数据。

代码如下:

#include <iostream>
#include <stdio.h>
using namespace std;
int v[1000]={0,1,3,5,9},w[1000]={0,2,3,4,7};//v存储价值,w存储重量 
//int v[1000]={0,2,4,2,1,10},w[1000]={0,2,12,1,1,4};//测试数据2 
int F[1000][1000]={0},i[1000][1000]={0},x[1000]={0};//优化函数F[k][y],代表只允许装前k种物品的最大价值 
void recall(int N,int B)//应用递归算法回溯输出实例的解
{
	int rank=1;//设置行数为第1行 
	while(N)//当N为0时,追溯结束 
	{
		if(rank>1)//当行数rank大于1行时 
		{
			cout<<'i'<<N<<'('<<B<<"-w"<<N<<")=";//输出iN(B-w[N]) 
			B-=w[N];//B=B-wN 
		}
		cout<<'i'<<N<<'('<<B<<")="<<i[N][B]<<"-> ";//输出iN(B)=i[N][B]-> 
		x[i[N][B]]++;//x[i[N][B]]加一 
		if(i[N][B]) cout<<'x'<<i[N][B]<<">="<<x[i[N][B]]<<',';//输出x[i[N][B]]>=x[i[N][B]]
		for(int t=N;t>i[N][B];t--)
			cout<<'x'<<t<<'='<<x[t]<<',';//输出i[N][B]+1到N-1的数据 
		N=i[N][B];//N=i[N][B]
		rank++;	//行数加一 
		cout<<endl;//换行 
	}
	
}
void bag(int n,int b)//执行函数装包 
{
	for(int k=1;k<=n;k++)//只允许装前k种物品 
	{
		for(int y=1;y<=b;y++)
		{
			if(k==1)//当只允许装第一种物品时,F1[y]=y/w1*v1 
			{
				F[1][y]=y/w[1]*v[1];//在背包足够时尽可能装多 
				if(w[1]<=y) i[1][y]=1;//如果第一种物品装得下,则i1[y]记录放下1号物品 
				else i[1][y]=0;//如果第一种物品装不下,则i1[y]设为0
				printf("F%d(%d)=%d     i%d(%d)=%d\n",k,y,F[k][y],k,y,i[k][y]);//格式输出 
			}
			else
			{
				if(w[k]<=y&&F[k-1][y]<=F[k][y-w[k]]+v[k])
				{//max{Fk[y],Fk[y-wk]+vk}
					F[k][y]=F[k][y-w[k]]+v[k];//Fk[y]=F[k][y-w[k]]+v[k]
					i[k][y]=k;//ik[y]记录放入k号物品 
				}
				else
				{
					F[k][y]=F[k-1][y];//Fk[y]=F[k][y-w[k]]+v[k]
					i[k][y]=i[k-1][y];//ik[y]记录放入i[k-1][y]号物品
				}
				//按格式输出步骤 
				printf("F%d(%d)=max{F%d(%d),F%d(%d-w%d)+v%d}=",k,y,k-1,y,k,y,k,k);//输出 
				printf("max{%d,F%d(%d-%d)+%d}=",k,k,y,w[k],v[k]);//输出 
				cout<<"max{"<<F[k-1][y]<<',';
				if(w[k]>y) cout<<"-∞";//如果wk>y,则Fk(y-wk)=-∞
				else cout<<F[k][y-w[k]];//否则输出数字 
				cout<<'+'<<v[k]<<"}="<<F[k][y];
				cout<<"     i"<<k<<'('<<y<<")=";
				if(w[k]>y||F[k-1][y]>F[k][y-w[k]]+v[k]) cout<<'i'<<k-1<<'('<<y<<")=";
				cout<<i[k][y]<<endl;//输出ik(y)
			}
		}
		cout<<endl;
	}
	cout<<"得出可装入物品的最大价值为"<<F[n][b]<<endl;
	cout<<"追踪解的过程:"<<endl;
	recall(n,b);//追踪解的过程函数
	cout<<"最终得到的实例解为:"; 
	for(int t=1;t<=n;t++)
	{
		if(t==n)  cout<<'x'<<t<<'='<<x[t]<<'.'<<endl;//最后一个输出句号,换行 
		else  cout<<'x'<<t<<'='<<x[t]<<',';
	}
	
}
int main()
{
	int n=4,b=10;//n代表物品总数,b代表背包容量
	//int n=5,b=15;//测试数据2 
	bag(n,b);//将物品总数n和背包容量b代入执行函数装包 
}

输出结果

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值