经典的算法九讲在此不做介绍,详细可参照https://www.cnblogs.com/jbelial/articles/2116074.html。
下面,主要讲我在看此博客时的经历和详细代码实现:
1.01背包问题
01背包问题,N个物品,费用costs[i],价值values[i],最多花费V,每个物体最多可选一次,求最大价值
故可用f[i][v]表示处理前i个物品消费恰好为v的价值
则状态转移方程为:f[i][v]=max{f[i-1][v-costs[i]]+values[i],f[i-1][v]};
(1)时间复杂度O(VN),空间复杂度O(VN)的实现,,此法可顺便将买下哪些物品求出,方法:
vector<int> getMaxPathOne(vector<int>&costs, vector<int>& values, int V)
{
vector<int> path;
vector<int> tmpRes(V+1,0);
vector<vector<int>> results(costs.size(),tmpRes);
//只考虑第一个物品时,只有当消费恰好为costs[0]时,才可将该物品买下,否则消费不能满足“恰好”两字
results[0][costs[0]] = values[0];
for (int i=1;i<costs.size();++i)
{
for (int j=0;j<=V;++j)
{
if (j>=costs[i])//如果消费恰好为j时,买得起第i个物体
{
results[i][j] = max(results[i - 1][j - costs[i]] + values[i], results[i - 1][j]);
}
else//如果买不起
{
results[i][j] = results[i - 1][j];
}
}
}
int value = 0;
for (int i=1;i<=V;++i)//找出对所有物体进行处理时,价值最大时的消费是多少
{
if (results[costs.size()-1][i]>results[costs.size()-1][value])
{
value = i;
}
}
//接下来找出要放哪些物体
int i = costs.size() - 1, v = value;
while (i>0)
{
if (results[i][v]!=results[i-1][v])//如果消费为v时,处理前i个物体与处理前i-1个物体的最大价值相同,
//则第i个物品是不被放入的;反之,则放入
{
path.push_back(costs[i]);
v = v - costs[i];
}
--i;
}
if (v>=costs[0])//对于第1个物体,看剩下的钱能不能买第一件,若够,就买
{
path.push_back(costs[0]);
}
return path;
}
(2)算法九讲中对01背包的空间优化方法,时间复杂度O(V*N),空间复杂度O(N):
f[v]表示花费最大为v时的最大价值,则i从低处循环时可得f[v]=max{f[v],f[v-costs[i]]+values[i]},max中的
f[v]代表处理前i-1个物体,消费不超过v时的最大价值
int getMaxValueOne(vector<int>& costs, vector<int>& values,int V)
{
vector<int> results(V+1,0);//花费最大为0~V时的价值
for (int i=0;i<=V;++i)//只考虑第一个物品时,允许花费范围为0~V
{
if (i>=costs[0])
{
results[i] = values[0];
}
}
for (int i=1;i<costs.size();++i)//恰好处理前i个物品时,允许花费范围为0~V
{
for (int j=V;j>=costs[i];--j) //保证第i次循环结束后f[v]中表示的就是我们定义的状态f[v]
{
//前i个物品消费恰好为j的价值=max(不买第i个物品消费恰好为j的价值,买第i个物品消费恰好为j的价值)
results[j] = max(results[j],results[j-costs[i]]+values[i]);//因为i从小循环上来,故max中的results[j]即为result[j-1]
}
}
return results[V];
}
2.完全背包问题
N个物品,费用costs[i],价值values[i],最多花费V,每个物体可选无数次,求最大价值。
f[i][v]表示消费恰好为v,在处理第i个物体时的最大价值,f[i][v]=max{f[i-1][v],f[i][v-costs[i]]+values[i]};
(1)时间复杂度O(VN),空间复杂度O(VN)的实现,,此法可顺便将买下哪些物品求出,方法:
vector<int> getMaxPathAbso(vector<int>&costs, vector<int>&values, int V)
{
vector<int> tmpRes(V+1,0);
vector<vector<int>> results(costs.size(),tmpRes);
for (int i = 1; costs[0]*i <= V; ++i)//只考虑第一个物品时,消费恰好为costs[0]的整数倍时,才可进行消费
{
results[0][costs[0] * i] = values[0] * i;
}
for (int i=1;i<costs.size();++i)
{
for (int j=0;j<=V;++j)
{
if (j>=costs[i])
{
results[i][j] = max(results[i-1][j],results[i][j-costs[i]]+values[i]);
}
else
{
results[i][j] =results[i-1][j];
}
}
}
int size = costs.size();
int maxCost = 0;//处理前i个物体时,价值最大时的消费
for (int i=0;i<=V;++i)
{
if (results[size-1][i]>results[size-1][maxCost])
{
maxCost = i;
}
}
vector<int> path;
//接下来找出要放哪些物体
int i = costs.size() - 1, v = maxCost;
while (i > 0)
{
if (results[i][v] != results[i - 1][v])//如果消费为v时,处理前i个物体与处理前i-1个物体的最大价值相同,
//则第i个物品是不被放入的;反之,则放入
{
path.push_back(costs[i]);
v = v - costs[i];
++i;
}
--i;
}
if (v % costs[0]==0)//对于第1个物体,看剩下的钱能不能买第一件,若够,就买
{
int k = v / costs[0];
while (k>0)
{
path.push_back(costs[0]);
--k;
}
}
return path;
}
(2)优化方法,时间复杂度O(NV),空间复杂度O(V)的方法:
int getMaxValueAbso(vector<int>&costs, vector<int>&values, int V)
{
vector<int> results(V+1,0);
for (int i = 0; i <= V; ++i)//只考虑第一个物品时,允许花费范围为0~V
{
results[i] = values[0]*(i/costs[0]);
}
for (int i=1;i<costs.size();++i)
{
for (int j=0;j<=V;++j)
{
if (j>=costs[i])
{
results[j] = max(results[j],results[j-costs[i]]+values[i]);
}
}
}
return results[V];
}