背包问题确实是一个让人有点费解的问题,它属于一种动态规划算法。以前确实也没有完全真正理解过这个算法,最近回头仔细品味,自觉有点心得领悟。将书本上的此算法根据自己的理解稍加整理,以一种更适合让人理解的方式呈现出来。
#include<stdio.h>
const int N=100;
int limitw;
int totv;
int maxv;
int option[N];
int cop[N];
struct Product
{
int weight;
int value;
} a[N];
int n;
void find(int i, int tw)
{
int k;
//1, i物品加入到当前的组合(条件是i物品的重量加上当前组合前面的所有物品的重量之和小于规定的总重量)
if (tw+a[i].weight <= limitw) {
cop[i] = 1;//标记第i个物品加入到当前的组合
if (i < n-1) {
find(i+1, tw+a[i].weight);//加上i的重量
} else {//否则的话i=n-1,一个完整的组合完毕
int sum = 0;
for (k = 0;k < n;k++) {
if(cop[k]) {
sum += a[k].value;
}
}
if (sum > maxv) {//如果当前组合的总值大于之前组合的总值则更新maxv
for (k = 0;k < n;k++) {
option[k] = cop[k];
}
maxv = sum;
}
}
cop[i] = 0;//find返回了表示i加入到当前组合的case已经递归完毕,现在退回到当前的函数栈来了。所以恢复i的标记,为下面的i不加入当前组合的case做准备
}
//0,i物品不加入到当前的组合是不需要任何条件。这里我让每个物品都走了不加入当前组合的case是为了便于理解,但是牺牲了些效率(因为有些情况如果i物品不加入的话则当前组合的可能总值一定会比已经存在的maxv要小,所以这种组合其实没必要走下去)
if(i < n-1) {
find(i+1, tw);
} else {
int sum = 0;
for (k = 0;k < n;k++) {
if(cop[k]) {
sum += a[k].value;
}
}
if (sum > maxv) {
for (k = 0;k < n;k++) {
option[k] = cop[k];
}
maxv = sum;
}
}
}
int main(int argc, _TCHAR* argv[])
{
int k,w,v;
printf("物品总数: ");
scanf("%d",&n);
for(k=0;k < n;k++)
{
printf(" 第%d种物品(重量,价值) : ", k+1);
scanf("%d, %d", &w, &v);
a[k].weight = w;
a[k].value = v;
}
printf(" 背包所能承受的总总量: ");
scanf("%d",&limitw);
maxv = 0;
for(k=0;k < n;k++) {
cop[k] = 0;
}
find(0, 0);
printf(" 最佳装填方案是: \n");
for(k=0;k < n;k++) {
if(option[k]) printf(" 第%d种物品\n", k+1);
}
printf(" 总价值=%d\n", maxv);
}
执行结果如下:
对于当前的组合,每一个物品都有两种分支,选择加入当前组合或者不加入当前组合。那么一共有n个物品的话,则可以有2的n次方个不同组合。所以我们的程序通过递归的方式覆盖了这个2的n次方个组合,然后将这2的n次方个组合的maxv进行比较得到其中最大的那个值。思路就是这么简单。
在这2的n次方个组合中,由于对组合物品的重量有限制,所以每次在选在第i个物品加入当前组合时就需要做一个是否超重的判断。