完全背包问题:已知有n中物品和一个容量为v的背包,每种物品均有无限件,第i种物品的费用为c[i],价值是w[i]
求如何放使得物品费用总和不超过背包容量,且价值总和最大。
求如何放使得物品费用总和不超过背包容量,且价值总和最大。
方法1:
仿照01背包,对每件物品的取件数再穷举,即确定每件物品的件数范围,然后当做01背包处理。
f[i][j] = max{f[i-1][v-k*c[i]] + k*w[i]} 0<=k*c[i]<=v
仿照01背包,对每件物品的取件数再穷举,即确定每件物品的件数范围,然后当做01背包处理。
f[i][j] = max{f[i-1][v-k*c[i]] + k*w[i]} 0<=k*c[i]<=v
方法2:(优化)
动归方程的优化。
f[i][j] = max{f[i-1][v],f[i][v-c[i]]+w[i]} 第i中物品放与不放
公式分析:在加选第i件物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]]
时间复杂度为O(n*v)
动归方程的优化。
f[i][j] = max{f[i-1][v],f[i][v-c[i]]+w[i]} 第i中物品放与不放
公式分析:在加选第i件物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]]
时间复杂度为O(n*v)
初始化:若不是恰好装满f[i][0]和f[0][j]均初始化为0
若是恰好装满f[i][0]均初始化为0, f[0][j](j>=1)初始化为无穷inf
若是恰好装满f[i][0]均初始化为0, f[0][j](j>=1)初始化为无穷inf
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 105
using namespace std;
const int inf = -0x3f3f3f3f;
int n, v;
int dp[N][N], c[N], w[N];
void init()
{
for (int i = 0; i <= n; i++)
dp[i][0] = 0;
for (int i = 1; i <= n; i++)
dp[0][i] = 0;
}
int main()
{
scanf("%d%d", &n,&v);
for (int i = 1; i <= n; i++)
scanf("%d%d", &c[i], &w[i]);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= v; j++)
{
if (j < c[i])//不能放
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - c[i]] + w[i]);
}
}
printf("%d\n", dp[n][v]);
return 0;
}
滚动数组实现。
动归方程
for i: 1->n
for j: c[i]->v
do f[j] = max{f[j],f[j-c[i]]+w[i]}
与零一背包的区别主要是第二层循环是正序,原因在于完全背包的特点是每件物品可以选无限件,因此加选第i件
物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]],所以用正循环。
动归方程
for i: 1->n
for j: c[i]->v
do f[j] = max{f[j],f[j-c[i]]+w[i]}
与零一背包的区别主要是第二层循环是正序,原因在于完全背包的特点是每件物品可以选无限件,因此加选第i件
物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]],所以用正循环。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 100005
using namespace std;
int n, v;//物品的种数,背包的容量
int dp[N], c[N], w[N];
void init()
{
for (int i = 0; i <= n; i++)
dp[i] = 0;
}
int main()
{
while (scanf("%d%d", &n, &v))
{
init();
for (int i = 1; i <= n; i++)
scanf("%d%d", &c[i], &w[i]);
for (int i = 1; i <= n; i++)
for (int j = c[i]; j <= v; j++)
dp[j] = max(dp[j], dp[j - c[i]] + w[i]);//表示放与不放
printf("%d\n", dp[v]);
}
return 0;
}