A.0-1背包问题:
n件物品重W[1:n],价值V[1:n],背包容量为C
1.动态规划:
-
动态规划即找准dp的表达式,分析0-1背包问题是需要找到背包最多能装多大价值的物品,那么此处我们的dp[i][j],就如此来定义:
dp[i][j]代表前i件物品,装入容量为j的背包的最大价值,比如dp[0][j] = 0, dp[i][0] = 0,dp[n][C]就是我们最后要求的装入前n件物品于容量为C的背包的最大价值。那么dp应该怎样往后一步一步算呢?
dp[i][j]是指前i件物品装入容量为j的背包的最大价值,那么就有两种选择项:
a.将第i件物品装进去,那么就必须得要装入i之前的这个容量j还能装得下第i件物品,即j > W[i],此时的dp[i][j] = dp[i - 1][j - W[i]] + V[i],也就是说,要把i装进去,那么i- 1的容量就只能用j - W[i]的容量来参与计算;
b.第i件物品就不放进去了,那dp[i][j] = dp[i-1][j];
比较以上两者取最大值,代码如下:
int dp[n + 1][C + 1];
for(i = 0; i <= n; i++)
dp[i][0] = 0;
for(j = 0; j <= C; j++)
dp[0][j] = 0;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= C; j++)
{
dp[i][j] = dp[i - 1][j];
if(j > W[i])
{
int pt = dp[i - 1][j - W[i]] + V[i];
if(pt > dp[i][j])
dp[i][j] = pt;
}
}
}
还有一种更简便的算法,这里只给出代码:
int g[C];
memeset(g,0,sizeof(g));
for(i = 1; i <= n; i++)
{
for(j = C; j >= 1; j--)
{
if(j > W[i])
{
pt = g[j - W[i]] + V[i];
if(pt > g[j])
g[i] = pt;
}
}
}
2.回溯:
-
回溯就是选还是不选的问题, 我们要考虑到每一种组合,考虑每一个物品的选择和不选择的问题, 在遍历的过程中,我们寻求合适的剪枝(约束条件和限界条件)
先看遍历代码:
void traceback(int t)
{
if(t > n)
{
if(sum > ans)
ans = sum;
return;
}
if(pt + W[t] <= C)
{
pt += W[t];
sum += V[t];
traceback(t + 1);
pt -= W[t];
sum -= V[t];
}
traceback(t + 1);
return;
}
上述代码我们的约束条件有了, 如果想进一步减少计算复杂度的话,我们可以再考虑加一个限界条件:
用r[i]记录到第i行的时候,一共有多少价值,即:
r[0] = 0;
r[1] = V[1];
r[i] = r[i - 1] + V[i];
新的回溯代码:
void traceback(int t)
{
if(t > n)
{
if(sum > ans)
ans = sum;
return;
}
if(sum + r[N] - r[t - 1] < ans)
return;
if(pt + W[t] <= C)
{
sum += V[t];
pt += W[t];
traceback(t + 1);//x[t] = 1;
sum -= V[t];
pt -= W[t];
}
traceback(t + 1);//x[t] = 0;
return;
}
B.分数背包问题:
n件物品重W[1:n],价值为V[1:n],背包容量为C,与0-1背包问题不同的是,分数背包问题取物品的时候可以取0 ~ W[i]重的物品,当然不取完,得到的价值也不会是全部的,也是根据取得量来定的。
贪心算法:
由于每件物品不需要全部取走,可以取一部分,那么我们就可以来计算没意见物品的“性价比”,看取多少重量的某件物品能够获得最大的价值,因此我们就再计算一个数组X[i] = V[i] / W[i],对数组X进行由大到小的排序,从大的往小的取,大的全部装进去,直到最后装不下了为止。
C.最优装载:
n件物品重W[1:n],背包容量为C,怎么取物品能使得取得件数最多。
贪心算法:
对每件物品按照重量由小到大排序,按重量与小达到往背包里面装物品,直到背包里面装不下为止。
D.装载问题:
n件物品重W[1:n],背包容量为C,怎么取能使得装载重量最大。
回溯算法:
每件物品有两个选择,装进去或者不装进去,在我们的回溯遍历中,就需要体现出这里有两种选择:
void traceback(int t)
{
if(t > n)
{
if(sum > ans)
ans = sum;
return;
}
if(sum + W[t] <= C)
{
sum += W[t];
traceback(t + 1);//x[t] = 1;
sum -= W[t];
}
traceback(t + 1);//x[t] = 0;
return;
}
如同0-1背包问题一样,这里只是有不同的选择条件,为了简化算法,我们应该添加限界条件:
r[0] = 0;
r[1] = W[i];
r[i] = r[i - 1] + W[i];
void traceback(int t)
{
if(t > n)
{
if(sum > ans)
ans = sum;
return;
}
if(sum + r[N] - r[t - 1] < ans)
return;
if(sum + W[i] <= C)
{
sum += W[i];
traceback(t + 1);//x = 1;
sum -= W[i];
}
traceback(t + 1);//x = 0;
return ;
}