01背包
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
O(nm)
int n, m;//m为容量
int dp[N];//dp[x]存储背包容量为x时的最大价值
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int v, w;//v为体积,w为价值
cin >> v >> w;
for (int j = m; j >= v; j--)//倒序循环防止重复计算
{
dp[j] = max(dp[j - v] + w, dp[j]);
}
}
cout << dp[m] << endl;
return 0;
}
完全背包
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
O(nm)
int n, m;
int v, w, dp[N];
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
{
cin >> v >> w;
for (int j = v; j <= m; j++)
{
dp[j] = max(dp[j], dp[j - v] + w);
}
}
cout << dp[m] << endl;
return 0;
}
多重背包(朴素)
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
O(snm)
int n, m;
int v, w, s, dp[N];
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
{
cin >> v >> w >> s;
while (s--)//直接拆成s个单一物品
{
for (int j = m; j >= v; j--)
{
dp[j] = max(dp[j], dp[j - v] + w);
}
}
}
cout << dp[m] << endl;
return 0;
}
多重背包(二进制优化)
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
O(log(s)nm)
int n, m;
int s, cnt, dp[N], v[logs * N], w[logs * N];//每个物品可拆成logs(+1)堆
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int a, b, s;
cin >> a >> b >> s;
for (int k = 1; k <= s; k *= 2)//二进制拆分
{
s -= k;
v[cnt] = a * k;
w[cnt++] = b * k;
}
if (s)
{
v[cnt] = a * s;
w[cnt++] = b * s;
}
}
for (int i = 0; i < cnt; i++)
{
for (int j = m; j >= v[i]; j--)
{
dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
}
}
cout << dp[m] << endl;
return 0;
}
多重背包(单调队列优化)
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
O(NV)
int n, m;
int dp[N], g[N], q[N];
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
while (n--)
{
int v, w, s;
cin >> v >> w >> s;
for (int j = 0; j < v; j++)
{
memcpy(g, dp, sizeof dp);//缓存上一轮的结果
int hh = 0, tt = -1;
for (int k = j; k <= m; k += v)
{
dp[k] = g[k];
if (hh <= tt && k - s * v > q[hh])
{
hh++;
}
if (hh <= tt)
{
dp[k] = max(dp[k], g[q[hh]] + (k - q[hh]) / v * w);
}
while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w)
{
tt--;
}
q[++tt] = k;
}
}
}
cout << dp[m] << endl;
return 0;
}
分组背包
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
O(snm)
int n, m;
int s[N], v[N][N], w[N][N], dp[N];
int main()
{
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)
{
cin >> s[i];
for (int j = 0; j < s[i]; j++)
{
cin >> v[i][j] >> w[i][j];
}
}
for (int i = 0; i < n; i++)
{
for (int j = m; j >= 0; j--)
{
for (int k = 0; k < s[i]; k++)//把同一组的每个物品都试一遍
{
if (v[i][k] <= j)
{
dp[j] = max(dp[j], dp[j - v[i][k]] + w[i][k]);
}
}
}
}
cout << dp[m] << endl;
return 0;
}