有n个物品,每个物品的重量为w[i],价值为v[i],购物车容量为W。选若干个物品放入购物车,在不超过容量的前提下使获得的价值最大。
(1)分析最优解的结构特征
(2)建立具有最优值的递归式
可以对每个物品依次检查是否放入或者不放入,对于第i个物品的处理状态:用ci表示前i件物品放入一个容量为j的购物车可以获得的最大价值。
那么问题就转化为了“前i-1件物品放入容量为j-w[i]的购物车中”,此时能获得的最大价值就是ci-1],再加上放入第i件物品获得的价值v[i]。即ci-1]+v[i]。
购物车容量不足,肯定不能放入;购物车容量组,我们要看放入、不放入哪种情况获得的价值更大。
所以,递归函数可以写为:
ci=ci-1(当ji);
ci=max{ci-1]+v[i],ci-1}(当j>wi)
(1)确定合适的数据结构
采用一维数组w[i]、v[i]分别记录第i个物品的重量和价值;二维数组用ci表示前i个物品放入一个容量为j的购物车可以获得的最大价值。
(2)初始化
初始化c[][]数组0行0列为0,其中i=01,2,...,n,j=0,1,2,...,W。
(3)循环阶段
(4)构造最优解
cn就是不超过购物车容量能放入物品的最大价值。如果还想知道具体放入了哪些物品,就需要根据c[][]数组逆向构造最优解,我们可以用一维数组x[i]来存储解向量。
假设现在有5个物品,每个物品重量为(2,5,4,2,3),价值为(6,3,5,4,6),购物车容量为10。
c[][]如下表:
c[][] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
2 | 0 | 0 | 6 | 6 | 6 | 6 | 6 | 9 | 9 | 9 | 9 |
3 | 0 | 0 | 6 | 6 | 6 | 6 | 11 | 11 | 11 | 11 | 11 |
4 | 0 | 0 | 6 | 6 | 10 | 10 | 11 | 11 | 15 | 15 | 15 |
5 | 0 | 0 | 6 | 6 | 10 | 12 | 12 | 16 | 16 | 17 | 17 |
所以最大价值为cn=17。
首先读取c5>c4,说明第5个物品装入了购物车,即x[5]=1,然后j=10-w[5]=10-3=7
然后去c4;
c4=c3,说明第4个物品没有装入购物车,即x[4]=0;
然后去找c3,依次类推。
#include
#include
#include
using namespace std;
#define maxn 10000
#define M 105
int c[M][maxn];//c[i][j] 表示前i个物品放入容量为j购物车获得的最大价值
int w[M],v[M];//w[i] 表示第i个物品的重量,v[i] 表示第i个物品的价值
int x[M]; //x[i]表示第i个物品是否放入购物车
int main(){
int i,j,n,W;//n表示n个物品,W表示购物车的容量
cout << "请输入物品的个数 n:";
cin >> n;
cout << "请输入购物车的容量W:";
cin >> W;
cout << "请依次输入每个物品的重量w和价值v,用空格分开:";
for(i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(i=1;i<=n;i++)//初始化第0列为0
c[i][0]=0;
for(j=1;j<=W;j++)//初始化第0行为0
c[0][j]=0;
for(i=1;i<= n;i++)//计算c[i][j]
for(j=1;j<=W;j++)
if(j0;i--)
if(c[i][j]>c[i-1][j])
{
x[i]=1;
j-=w[i];
}
else
x[i]=0;
cout<<"装入购物车的物品序号为:";
for(i=1;i<=n;i++)
if(x[i]==1)
cout<
1.算法复杂度分析
(1)时间复杂度:O(n*W)
(2)空间复杂度O(n*W)
2.算法优化改进
使用一个数组dp[]保证第i次循环结束后dp[j]中表示的就是我们定义的ci。
所以代码如下:
void opt1(int n,int W)
{
for(i=1;i<=n;i++)
for(j=W;j>0;j--)
if(j>=w[i]) //当物品的重量大于购物车的容量,比较此物品放与不放是否能使得购物车内的价值最大
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
我们可以缩小范围,因为只有当购物车的容量大于等于物品重量的时候才要更新,所以代码如下:
void opt2(int n,int W)
{
for(i=1;i<= n;i++)
for(j=W;j>=w[i];j--)
//当物品的重量大于购物车的容量
//比较此物品放与不放是否能使得购物车内的价值最大
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
我们还可以再缩小范围,确定搜索的下界bound
void opt3(int n,int W)
{
int sum[n];//sum[i]表示从1...i的物品重量之和
sum[0]=0;
for(i=1;i<=n;i++)
sum[i]=sum[i-1]+w[i];
for(i=1;i<=n;i++)
{
int bound=max(w[i],W-(sum[n]-sum[i-1]));
//w[i]与剩余容量取最大值,
//sum[n]-sum[i-1]表示从i...n的物品重量之和
for(j=W;j>=bound;j--)
//当物品的重量大于购物车的容量
//比较此物品放与不放是否能使得购物车内的价值最大
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
}
}
递归表达式:
ci=0(j=i-1);
ci=min{ci+ck+1}+wi(j≥i)
wi=qi-1(j=i-1);
wi=wi+pj+qj
(1)确定合适的数据结构
一维数组:p[]、q[]分别表示实结点和虚结点的搜索概率
二维数组:ci表示最优二叉搜索树T(i,j)的搜索成本,wi表示最优二叉搜索树T(i,j)中的所有实结点和虚结点的搜索概率之和,si表示最优二叉搜索树T(i,j)的根节点序号。
(2)初始化。ci=0.0,wi=q[i-1],其中i=1,2,3,...,n+1。
(3)循环阶段。
(4)构造最优解。
w[][] | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
1 | 0.06 | 0.18 | 0.37 | 0.52 | 0.59 | 0.76 | 1.00 |
2 | 0.08 | 0.27 | 0.42 | 0.49 | 0.66 | 0.90 | |
3 | 0.10 | 0.25 | 0.32 | 0.49 | 0.73 | ||
4 | 0.07 | 0.14 | 0.31 | 0.55 | |||
5 | 0.05 | 0.22 | 0.46 | ||||
6 | 0.05 | 0.29 | |||||
7 | 0.10 |
c[][] | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
1 | 0 | 0.18 | 0.55 | 0.95 | 1.23 | 1.76 | 2.52 |
2 | 0 | 0.27 | 0.67 | 0.90 | 1.38 | 2.09 | |
3 | 0 | 0.25 | 0.46 | 0.94 | 1.48 | ||
4 | 0 | 0.14 | 0.45 | 0.98 | |||
5 | 0 | 0.22 | 0.68 | ||||
6 | 0 | 0.29 | |||||
7 | 0 |
s[][] | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
1 | 1 | 2 | 2 | 2 | 3 | 5 | |
2 | 2 | 2 | 3 | 3 | 5 | ||
3 | 3 | 3 | 3 | 5 | |||
4 | 4 | 5 | 5 | ||||
5 | 5 | 6 | |||||
6 | 6 | ||||||
7 |
void Optimal_BST()
{
for(i=1;i<=n+1;i++)
{
c[i][i-1]=0.0;
w[i][i-1]=q[i-1];
}
for(int t=1;t<=n;t++)//t为关键字的规模
//从下标为i开始的关键字到下标为j的关键字
for(i=1;i<=n-t+1;i++)
{
j=i+t-1;
w[i][j]=w[i][j-1]+p[j]+q[j];
c[i][j]=c[i][i-1]+c[i+1][j];//初始化
s[i][j]=i;//初始化
//选取i+1到j之间的某个下标的关键字作为
//从i到j的根,如果组成的树的期望值当前最小
//则k为从i到j的根节点
for(k=i+1;k<=j;k++)
{
double temp=c[i][k-1]+c[k+1][j];
if(temp1E-6)
//C++中浮点数因为精度问题不可以直接比较
{
c[i][j]=temp;
s[i][j]=k;//k即为从下标i到j的根节点
}
}
c[i][j]+=w[i][j];
}
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
if(flag==0)
{
cout<<"S"<=j)
{
cout<<"e"<
时间复杂度为O(n3),空间复杂度为O(n2)。
又可以用四边形不等式优化(后续研究一下)
时间复杂度减少到O(n2)。
void Optimal_BST()
{
for(i=1;i<=n+1;i++)
{
c[i][i-1]=0.0;
w[i][i-1]=q[i-1];
}
for(int t=1;t<=n;t++)//t为关键字的规模
//从下标为i开始的关键字到下标为j的关键字
for(i=1;i<=n-t+1;i++)
{
j=i+t-1;
w[i][j]=w[i][j-1]+p[j]+q[j];
int i1=s[i][j-1]>i?s[i][j-1]:i;
int j1=s[i+1][j]1E-6)
//C++中浮点数因为精度问题不可以直接比较
{
c[i][j]=temp;
s[i][j]=k;//k即为从下标i到j的根节点
}
}
c[i][j]+=w[i][j];
}
}
void Construct_Optimal_BST(int i,int j,bool flag)
{
if(flag==0)
{
cout<<"S"<=j)
{
cout<<"e"<
动态规划关键总结如下:
最优子结构判定
如何得到最优解递归式