您的位置:首页 > 编程语言 > C语言/C++

c++动态规划——背包问题

2016-11-08 09:55 281 查看
问题基础:有N件物品和一个容量为C的背包。第i件物品的体积是W[i],价值是V[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。


怎样才能得到放入书包物品的的最大价值呢?

解决方法——【动态规划】

运筹学的分支,纠结决策过程最优化的数学方法

把多阶段问题分解为相互联系单一阶段小问题求解,上一阶段的决策可以对下一阶段的决策产生影响

各个阶段的决策选择最优,从而使整个过程达到最优

令Vi、Wi 分别表示第i个物品的价值和体积,V(i,j)表示前i个物品能装入背包容量为j的背包的最大价值,有以下动态规划函数:



算法大概:即填写二维矩阵,行数为物品个数加1,列数为背包承重加1

例如,有5个物品,其重量分别是{2, 2, 6, 5, 4},价值分别为{6, 3, 5, 4, 6},背包的容量为10,求装入背包的物品和获得的最大价值。

第一阶段,只装入前1个物品,要求能够得到最大价值;

第二阶段,只装入前2个物品,要求能够得到最大价值;这个问题求解要在第一阶段最优解的基础上进行;

依此类推,直到第n个阶段。

最后,V(n,C)便是在容量为C的背包中装入n个物品时取得的最大价值。

每个阶段问题的求解都是基于前一个阶段的解是最优的基础上。



每次增加一个物品放入背包,如果可以放入,设该物品重量为w1,价值v1,得到其放入之后背包的总重量W,减去这个物品的重量w1,那么W-w1这个重量的列的最大价值加上v1如果大于该位置(该行该列)上一行的价值的值,就更新此处的value值,表明这个物品放入背包,不然就继承上一行的value值。

直接上代码:

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<queue>
#include<climits>
#include<cstring>
#include<stdlib.h>
#include <time.h>
#include<ctime>
#include <iomanip>
using namespace std;

void Add(int *m[],int weight[],int value[],int num,int content)//n代表物品的个数
{
int i,j;
for(j=0;j<=content;j++)//采用从底到顶的顺序来设置m[i][j]的值,首先放weight[num]
{
if(j<weight[num])//j小于weight[num],所对应的值设为0
{
m[num][j] = 0;
}
else//否则就为可以放置
{
m[num][j]=value[num];
}
}

for(i=num-1;i>=1;i--)//对剩下的num-1个物品进行放置
{
for(int j=0;j<=content;j++)
{
if(j<weight[i])//如果j<weight[i]则当前位置就不能放置,它等于上一个位置的值。
{
m[i][j]=m[i+1][j];
}
else//否则,就比较到底是放置之后的值大,还是不放置的值大,选择其中较大者。
{
m[i][j]=m[i+1][j]>m[i+1][j-weight[i]]+value[i]?m[i+1][j]:m[i+1][j-weight[i]]+value[i];
}
}
}
}
void putout(int *m[],int num,int content,int x[],int weight[])
{
int j=content,i;
for(i=1;i<=num-1;i++)
{
if(m[i][j] == m[i+1][j])
{
x[i]=0;//0表示不放进背包该物品
}
else
{
x[i]=1;//放进背包该物品
j=j-weight[i];
}
}
x[num]=m[i][j]?1:0;
}
int main()
{
while(true){
int times=100000,t;//运行总次数
double alltime=0;
int num,content; //物品的个数和背包容量
cout<<"输入物品个数:";
cin>>num;
cout<<"输入背包容量:";
cin>>content;
int *weight=new int[num+1];//物品的重量,其中0号位置不使用 。
int *value=new int[num+1];//物品对应的待加,0号位置置为空。
int i,j;
for(t=0;t<times;t++)
{
clock_t start,finish;
srand((int)time(NULL));//srand()函数产生一个以当前时间开始的随机种子.应该放在for等循环语句前面 不然要很长时间等待
for(i=1;i<=num;i++)
{
//weight[i]=rand()%(content/2);//生成0~content/2的整数
//value[i]=rand()%(content/2);
weight[i]=1 + content * rand() / (RAND_MAX + 1);//生成1~content的随机数
value[i]=1 + content * rand() / (RAND_MAX + 1);
}

weight[0]=0;
value[0]=0;
/*cout<<setw(5)<<"编号"<<setw(5)<<"重量"<<setw(5)<<"价值"<<endl;
for(i=1;i<=num;i++)
{
cout<<setw(3)<<i<<setw(5)<<weight[i]<<setw(5)<<value[i]<<endl;
}
cout<<endl;*/

int *x=new int[num+1];
int **m = new int*[num+1];
for(i=0;i<num+1;i++)
{
m[i]=new int[content+1];
}
for(i=0;i<num+1;i++)
{
for(j=0;j<content+1;j++)
{
m[i][j]=0;
}
}

start=clock();//开始时间
Add(m,weight,value,num,content);
putout(m,num,content,x,weight);
finish=clock();//结束时间
alltime=alltime+(double)(finish-start);//times次计算的时间总和

/*for(i=0;i<=num;i++)
{
for(j=0;j<=content;j++)
{
printf("%2d ",m[i][j]);
}
cout<<endl;
}
cout<<"放入背包的物品编号:\n";
for(i=1;i<=num;i++)
{
if(x[i]==1)
{
cout<<i<<" ";
}
}
cout<<endl;*/
}
cout<<times<<"组数据平均运行时间:"<<alltime/times<<"ms"<<endl;//times次运行的平均时间,结果更精确
cout<<"-----------------------------------"<<endl;
}
return 0;
}
代码中还包括运行时间的计算,随机数的生成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息