背包dp(模版)
标题
1.01背包问题
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 1010;
int dp[M];
int main() {
int N, V, v, w;
scanf("%d%d", &N, &V);
for (int i = 1; i <= N; ++i) {
scanf("%d%d", &v, &w);
for (int j = V; j - v >= 0; --j) {
dp[j] = max(dp[j], dp[j - v] + w);
}
}
printf("%d", dp[V]);
return 0;
}
2.完全背包问题
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 1010;
int dp[M];
int main() {
int N, V, v, w;
scanf("%d%d", &N, &V);
for (int i = 1; i <= N; ++i) {
scanf("%d%d", &v, &w);
for (int j = v; j <= V; ++j) {
dp[j] = max(dp[j], dp[j - v] + w);
}
}
printf("%d", dp[V]);
return 0;
}
3.多重背包问题 I
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 110;
int dp[M];
int main() {
int N, V, v, w, s;
scanf("%d%d", &N, &V);
for (int i = 1; i <= N; ++i) {
scanf("%d%d%d", &v, &w, &s);
for (int j = V; j >= 0; --j) {
for (int k = 1; k <= s; ++k) {
if (j - k * v >= 0) {
dp[j] = max(dp[j], dp[j - k * v] + k * w);
}
}
}
}
printf("%d", dp[V]);
return 0;
}
4.多重背包问题 II
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 2020;
int dp[M];
int v[M], s[M], w[M], vv[M * 10], ss[M * 10], ww[M * 10];
int main() {
int N, V, cnt = 0;
scanf("%d%d", &N, &V);
for (int i = 1; i <= N; ++i) {
scanf("%d%d%d", &v[i], &w[i], &s[i]);
for (int j = 1; j <= s[i]; j = j << 1) {
vv[cnt] = v[i] * j;
ww[cnt++] = w[i] * j;
s[i] -= j;
}
if (s[i]) {
vv[cnt] = v[i] * s[i];
ww[cnt++] = w[i] * s[i];
}
}
for (int i = 0; i < cnt; ++i) {
for (int j = V; j - vv[i] >= 0; --j) {
dp[j] = max(dp[j], dp[j - vv[i]] + ww[i]);
}
}
printf("%d", dp[V]);
return 0;
}
/*
* 二进制优化
* s[i] = 1 * 2 * 4 *... * w[i] + temp;
*/
5.多重背包问题 III
一般来说二进制就够了,有兴趣可以看一下单调队列优化
#include <iostream>
#include <cstring>
using namespace std;
const int M = 20020;
int dp[M], dp1[M], que[M];
int main()
{
int N, V;
cin >> N >> V;
int w, v, s;
for (int i = 0; i < N; ++i) {
cin >> w >> v >> s;
memcpy(dp1, dp, sizeof(dp));
for (int j = 0; j < w; ++j) {
int head = 0, tail = -1;
for (int k = 0; j + k * w <= V; ++k) {
if (head <= tail && k - que[head] > s) head++;
while (head <= tail && dp1[j + k * w] - k * v >= dp1[j + que[tail] * w] - que[tail] * v)
tail--;
que[++tail] = k;
dp[j + k * w] = dp1[j + que[head] * w] + (k - que[head]) * v;
}
}
}
cout << dp[V] << endl;
return 0;
}
6.混合背包问题
#include <iostream>
#include <cstring>
using namespace std;
int dp[1010], w[1010], v[1010], s[1010], dp1[1010], que[1010];
int main()
{
int N, V;
cin >> N >> V;
for (int i = 0; i < N; ++i) {
cin >> w[i] >> v[i] >> s[i];
if (s[i] == -1)
s[i] = 1;
if (s[i] == 0) {
s[i] = V / w[i];
}
}
for (int i = 0; i < N; ++i) {
memcpy(dp1, dp, sizeof(dp));
for (int j = 0; j < w[i]; ++j) {
int head = 0, tail = -1;
for (int k = 0; j + k * w[i] <= V; ++k) {
if (head <= tail && k - que[head] > s[i]) head++;
while (head <= tail && dp1[j + k * w[i]] - k * v[i] >= dp1[j + que[tail] * w[i]] - que[tail] * v[i]) tail--;
que[++tail] = k;
dp[j + k * w[i]] = dp1[j + que[head] * w[i]] + (k - que[head]) * v[i];
}
}
}
cout << dp[V];
return 0;
}
7.二维费用的背包问题
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 1010;
int dp[M][M];
int main() {
int N, V, M, v, m, w;
scanf("%d%d%d", &N, &V, &M);
for (int i = 1; i <= N; ++i) {
scanf("%d%d%d", &v, &m, &w);
for (int j = V; j - v >= 0; --j) {
for (int k = M; k - m >= 0; --k) {
dp[j][k] = max(dp[j][k], dp[j - v][k - m] + w);
}
}
}
printf("%d", dp[V][M]);
return 0;
}
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
ll dp[330][330];
int main() {
int n, H, S;
scanf("%d%d%d", &n, &H, &S);
int h, s, w;
for (int i = 0; i < n; ++i) {
scanf("%d%d%d", &h, &s, &w);
for (int j = H; j - h > 0; --j) {
for (int k = S; k - s >= 0 || (j - h - abs(k - s) > 0); --k) {
if (k - s < 0) {
int js = abs(k - s);
dp[j][k] = max(dp[j][k], dp[j - h - js][k - s + js] + w);
} else dp[j][k] = max(dp[j][k], dp[j - h][k - s] + w);
}
}
}
printf("%lld", dp[H][S]);
return 0;
}
8.分组背包问题
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 110;
int dp[M];
int v[M], w[M];
int main() {
int N, V;
scanf("%d%d", &N, &V);
for (int i = 1; i <= N; ++i) {
int s;
scanf("%d", &s);
for (int j = 1; j <= s; ++j) {
scanf("%d%d", &v[j], &w[j]);
}
for (int j = V; j >= 0; --j) {
for (int k = 1; k <= s; ++k) {
if (j - v[k] >= 0)
dp[j] = max(dp[j], dp[j - v[k]] + w[k]);
}
}
}
printf("%d", dp[V]);
return 0;
}
9.有依赖的背包问题
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M = 110;
int N, V;
int next1[M], head[M], v1[M];
int v[M], w[M];
int dp[M][M]; // dp[i][j]: 选取i时背包容量为j时的最大价值
int cnt = 1, root;
void add(int start1, int end1) {
v1[cnt] = end1;
next1[cnt] = head[start1];
head[start1] = cnt++;
}
void dfs(int now) {
for (int i = head[now]; i; i = next1[i]) {
int son = v1[i];
dfs(son);
for (int j = V - v[now]; j >= 0; --j) {
for (int k = 0; k <= j; ++k) {
dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[son][k]);
}
}
}
for (int i = V; i >= v[now]; --i) {
dp[now][i] = dp[now][i - v[now]] + w[now];
}
for (int i = 0; i < v[now]; ++i) dp[now][i] = 0;
}
int main() {
scanf("%d%d", &N, &V);
int p;
for (int i = 1; i <= N; ++i) {
scanf("%d%d%d", &v[i], &w[i], &p);
if (p == -1) root = i;
else
add(p, i);
}
dfs(root);
printf("%d",dp[root][V]);
return 0;
}
10.背包问题求方案数
#include <cstdio>
#include <iostream>
using namespace std;
const int MOD = 1e9 + 7;
const int M = 1010;
int dp[M], g[M];
int main() {
int N, V, v, w;
scanf("%d%d", &N, &V);
for (int i = 0; i <= V; ++i) g[i] = 1; //不装入物品也是一种方案
for (int i = 0; i < N; ++i) {
scanf("%d%d", &v, &w);
for (int j = V; j >= v; --j) {
/* 多装入一件新物品,采用g[j - v]方案数 */
if(dp[j - v] + w > dp[j]) {
dp[j] = dp[j - v] + w;
g[j] = g[j - v];
} else if(dp[j - v] + w == dp[j]) { /* 可以采用装入新物品(g[j - v])或者不装入新物品(g[v]),采用g[j - v] + g[v] */
g[j] = (g[j] + g[j - v]) % MOD;
}
}
}
printf("%d\n", g[V]);
return 0;
}
11.背包问题求具体方案
#include <iostream>
#include <cstdio>
using namespace std;
const int M = 1010;
int v[M], w[M];
int dp[M][M]; //dp[i][j]表示从第i个物品到最后一个物品,装入容量为j时的背包的最优解
int main() {
int n, V;
scanf("%d%d", &n, &V);
for (int i = 1; i <= n; ++i)scanf("%d%d", &v[i], &w[i]);
for (int i = n; i >= 0; --i) {
for (int j = v[i] - 1; j >= 0; --j) {
dp[i][j] = dp[i + 1][j];
}
for (int j = V; j - v[i] >= 0; --j) {
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - v[i]] + w[i]);
}
}
for (int i = 1; i <= n; ++i) {
if (v[i] > V) continue;
if (dp[i][V] == dp[i + 1][V - v[i]] + w[i]) {
printf("%d ", i);
V -= v[i];
}
}
/*
如果dp[i][j] = dp[i + 1][j]:不选第i个物品时得到最优解
如果dp[i][j] = dp[i + 1][j - v[i]] +w[i]:必须选第i个物品才可以得到最优解
如果dp[i][j] = dp[i + 1][j] = dp[i + 1][j - v[i]] + w[i]:选不选第i个物品都可以得到最优解,但是为了字典序最小,也必须选择该物品
*/
return 0;
}