一、算法说明
1. 问题描述:存在一个背包和N个物品,已知背包的容量C,以及每件物品的重量Wi和价值Pi,每件物品或者完全放入背包或者完全不放入背包,要求选择放入背包的物品,使总重量不能超过背包的容量,同时使物品的总价值最大。
2. 算法分析:和背包问题不同,0-1背包问题无法用贪婪算法解决,故而采用动态规划法解决。
首先将一个0-1背包问题抽象,在上述问题中,用f[i][j]表示对于容量为j(0≤j≤C)的背包在前i(0≤i≤N)个物品中选取价值最大的物品组合时可以获得的最大价值,显而易见有:f[i][0] = f[j][0] = 0, 0≤i≤N, 0≤j≤C
之后,对于任何一个f[i][j],可以拆解为如下子问题:
f[i][j]=f[i-1][j] , if w[i]>j
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+p[i]) , if w[i]≤j
其所表达的含义是:对于某一容量,有i个物品的0-1背包问题看做由更小集合上(i-1个物品)的背包问题的延伸。若第i个物品的重量大于当前背包容量(w[i]>j),则该物品不能放入背包,则当前背包问题的解即为前i-1个物品的背包问题;若第i物品的重量小于等于当前背包容量(w[i]≤j),则判断将该物品放入的情况和不将该物品放入的情况,在两者中取最优解(较大者)。
3. 算法实现和性能分析
变量设置:
n: 物品的个数
c:背包的容量
w[n]:物品的重量,w[i]表示第i个物品的重量(0≤i≤n);
p[n]:物品的价值,p[i]表示第i个物品的价值(0≤i≤n);
x[n]:最大价值下,物品是否被选择,x[i]=1表示第i件物品被选择,x[i]=0表示第i件物品不被选择(0≤i≤n);
解决0-1背包问题的函数:
整个函数分为三个阶段,第一,初始化,设置f[i][0]=f[0][j]=0,时间复杂度为n+c,即O(n);第二阶段,逐次计算f[i][j],时间复杂度为n*c,即O(nc); 第三阶段,判断在最大价值下,每个物品是否被放入背包,时间复杂度为n,O(n);
故整体来看,空间复杂度为n*c+3n,时间复杂度也为n*c+3n。
具体函数如下:
int backge(int n, int w[], int p[], int x[],int c){
// 初始化
int i = 0;
for(i=0;i<c+1;i++){
f[0][i]=0;
}
int j=0;
for(j=0;j<n+1;j++){
f[j][0] =0;
x[j]=0;
}
//逐次计算f[i][j]
for(i=1;i<n+1;i++){
for(j=1;j<c+1;j++){
if(w[i-1]>j){
f[i][j]=f[i-1][j];
}else{
f[i][j]=max(f[i-1][j],f[i-1][j-w[i-1]]+p[i-1]);//max()函数事先定义
}
}
}
//判断在最大价值下,每个物品是否被放入背包
j=c;
for(i=n;i>0;i--){
if(f[i][j]>f[i-1][j]){
x[i-1]=1;
j =j -w[i-1];
}
}
return f[n][c];//返回最大价值
}
二:实验,结果统计及分析
实验数据设置:
c:固定800
n:随机生成15-40
w[i]:随机生成0-1000
p[i]:随机生成0-100
进行50次实验:
实验结果截图如下:
从实验数据来看,每次的运行时间都在一个毫秒左右,使用clock()无法统计更精确的运行时间,所以无法进行较为精确的分析。
全部代码如下:#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//the num of the goods less than 101 and the capaticy of the backage less that 1000;
int f[50][1000];
int x[50];
//the function which return the bigger one of a and b
int max(int a, int b){
if(a>b){
return a;
}else{
return b;
}
}
// the function for 0-1 backage problem
int backage(int n, int w[], int p[], int x[], int c){
int i = 0;
for(i=0;i<c+1;i++){
f[0][i]=0;
}
int j=0;
for(j=0;j<n+1;j++){
f[j][0] =0;
x[j]=0;
}
for(i=1;i<n+1;i++){
for(j=1;j<c+1;j++){
if(w[i-1]>j){
f[i][j]=f[i-1][j];
}else{
f[i][j]=max(f[i-1][j],f[i-1][j-w[i-1]]+p[i-1]);
}
}
}
j=c;
for(i=n;i>0;i--){
if(f[i][j]>f[i-1][j]){
x[i-1]=1;
j =j -w[i-1];
}
}
return f[n][c];
}
int main(){
int c = 800;
int result;
for(int count =0 ;count<50;count++){
srand(count);
int num = rand()%26+15;
//int num=45;
int p[num];
int w[num];
for( int i =0; i<num;i++){
w[i] = rand()%1000;
p[i] = rand()%100;
}
clock_t begin = clock();
result = backage(num,w,p,x,c);
clock_t finish = clock();
long interval = finish - begin;
printf("nums:%i ",num);
printf("begintime:%ld ",begin);
printf("finishtime:%ld ",finish);
printf("result:%i ", result);
printf("used time:%ld\n",interval);
}
}