//------------------------------------------------------------------------
//0/1背包问题
//有N件物品和一个容重为g的背包。假设第i件物品的重量是G[i],价值是V[i]。
//求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
//解题思路
//这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
//用子问题定义状态:即bag(i,g)表示前i件物品放入一个容重为g的背包可以获得的最大价值。
//则其递归函数便是:bag(i,g)=max{bag(i-1,g),bag(i-1,g-G[i])+V[i]} 。
#ifndef max
#define max(x,y) (x)>(y)?(x):(y)
#endif
#ifndef N
#define N 5//物品数量
#endif
int A[N];//选取某物品时可得到的最大价值
int B[N];//不选取某物品时可得到的最大价值
int G[N]={3,1,5,2,4};//存放物品重量//
int V[N]={10,3,14,5,11};//存放物品价值//
//参数n=物品号,g=容重,返值为所选物品总价值
//初值n=物品最大编号,g=背包最大容重
int bag1(int n,int g)
{
if(n<0) return 0;//终止条件
if(g<G[n]) return bag1(n-1, g);//不能放了
A[n] = bag1(n-1,g-G[n]) + V[n];//将n号物品放入
B[n] = bag1(n-1, g);//不将n号物品放入
return max(A[n], B[n]);//返回最大值
}
//完全背包问题
// 题目
// 有N种物品和一个容量为g的背包,每种物品都有无限数量可用。
// 第i种物品的重量是G[i],价值是V[i]。
// 求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
// 解题思路
// 这也是很基础的背包问题,特点是:每种物品都有无限多件,不仅可以选择放或不放,还可选择放多少。
// 用子问题定义状态:即bag(i,g)表示前i件物品放入一个容重为g的背包可以获得的最大价值。
// 则其递归函数便是:bag(i,g)=max{bag(i-1,g),bag(i,g-G[i])+V[i]} 。
// 两个递归函数差别在于i是否变化,此处i不变意味着下次依然可以选择第i种物品
//参数n=物品号,g=容重,返值为所选物品总价值
//初值n=物品最大编号,g=背包最大容重
int bag2(int n,int g)
{ if(n<0) return 0;//终止条件
if(g<G[n]) return bag2(n-1, g);//不能放了
A[n] = bag2(n,g-G[n]) + V[n];//将n号物品放入
B[n] = bag2(n-1, g);//不将n号物品放入
return max(A[n], B[n]);//返回最大值
};
//多重背包问题
// 题目
// 有N种物品和一个容量为g的背包,每种物品都有C[i]数量可用。
// 第i种物品的重量是G[i],价值是V[i]。
// 求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
// 解题思路
// 这也是很基础的背包问题,特点是:每种物品都有多件,但数量是有限的,可以选择放或不放,和放多少。
// 用子问题定义状态:即bag(i,g)表示前i件物品放入一个容重为g的背包可以获得的最大价值。
// 解题思路
// 可以化简为0/1背包问题
// 方法是:将第i种物品分成若干件物品,其中每件物品的重量和价值均是原来的重量和价值的K倍,
// K=1,2,4,...,2^(k-1),C[i]-2^k+1,且k是满足C[i]-2^k+1>0的最大整数。
// 例如,如果第i种物品的数量C[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。
// 分成的这几件物品的系数和为C[i],表明不可能取多于C[i]件的第i种物品。
// 程序略
//怎么知道选取了哪些物品,以及该物品的数量?
//根据bag1函数我们已经得到所选物品总价值Value
//于是可以在数组A[]中查找Value,
//假设A[i]>=Value而且B[i]<Value
//那么i号物品就一定在所选之列,其价值是V[i]
//Value-V[i]就是其它物品的价值总和,按以上方法
//即可查出其它物品
//对于bag2,还要确定数量,
//数量count=(A[i]-B[i])/V[i]
//打印出物品,bag1适用
void print1(int Value)
{ printf("所选物品的价值%d=",Value);
for(int i=N-1;i>=0;--i)
{ if(A[i]>=Value && B[i]<Value)
{ printf("%d+",V[i]);//价值
Value-=V[i];
}
}
printf("/b/n");
};
//打印出物品及数量,bag2适用
void print2(int Value)
{ printf("所选物品的价值%d=",Value);
for(int i=N-1;i>=0;--i)
{ if(A[i]>=Value && B[i]<Value)
{ int count=(A[i]-B[i])/V[i];
printf("%d*%d+",count,V[i]);//数量*价值
Value-=A[i]-B[i];
}
}
printf("/b/n");
};
void test()
{ int n=5,g=10;
int r;
r=bag1(n-1,g);
print1(r);
r=bag2(n-1,g);
print2(r);
}