DP学习笔记
\(DP\) 的动机
- 记忆化搜索
- 多阶段决策:背包中一个物品可以看作一个阶段,一个阶段的决策是选多少件物品。
多重背包和相关 \(DP\)
int main()
{
// cost[i], val[i]
for (int i = 1; i <= n; i ++)
{
// 继承,不选的情况
for (int j = 0; j <= V; j ++)
{
dp[i][j] = dp[i - 1][j];
}
// 刷表,更新父状态
for (int j = 0; j + cost[i] <= V; j ++)
{
dp[i][j + cost[i]] = max(dp[i][j + cost[i]], dp[i - 1][j] + val[i]);
}
//填表,搜集子状态
//可通过数据结构优化
for (int j = cost[i]; j <= V; j ++)
{
dp[i][j] = max(dp[i][j], dp[i - 1][j - cost[i]] + val[i]);
}
for (int j = 0; j < cost[i]; j ++)
{
tmp[j] = 0;
}
//强制选择
/*A[] + (c, v) = B[],把这件物品强制塞进去*/
for (int j = 0; j + cost[i] <= V; j ++)
{
tmp[j + cost[i]] = dp[j] + val[i];
}
for (int j = 0; j <= V; j ++)
{
dp[j] = max(dp[j], tmp[j]);
}
}
return 0;
}
int main()
{
// cost[i], val[i]
for (int i = 1; i <= n; i ++)
{
for (int j = 0; j <= V; j ++)
{
dp[i][j] = dp[i - 1][j];
}
for (int j = cost[i]; j <= V; j ++)
{
dp[i][j] = max(dp[i][j], dp[i - 1][j - cost[i]] + val[i]);
}
// |
// V
for (int j = V; j >= cost[i]; j --)
{
dp[j] = max(dp[j], dp[j - cost[i]] + val[i]);
}
}
return 0;
}
01 背包正确性:每次在 \(j\) 时,访问 j-cost[i] 是上一层的。
完全背包正确性:每层都在考虑之前DP的前缀。
二进制优化
20 件 = 1 + 2 + 4 + 8 + 5
可以构成 1 ~ 20 内的任意数字
捆绑物体来计算
只用 \(\log_{2}{x}\) 个物品集合表示 \(1\) ~ \(x\) 的所有数量
题目
B
二进制分组过不了
考虑 bitset
\(9876543210 \\ ----- \\ 0010011001\)
表示0,3,4,7这四个数可以被构成
若强制加入 \(2\),\(2, 5, 6, 9\) 可以被构成
\(0010011001\) 向左移动 \(2\) 位,再与原数或起来
\(\ \ \ 1001100100 \\ \& 0010011001 \\ ------ \\ \ \ \ 1011111101\)
bitset<1000005> dp;
int main()
{
int n, v;
cin >> n >> v;
while (n --)
{
scanf("%d", &x);
dp = (dp << x) | dp;
}
}
C
初始代码 \(198ms\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int NR = 30;
const int MR = 1010;
int a[NR];
int f[NR][MR][MR];
int main()
{
int n;
scanf("%d", &n);
int m = 0;
for (int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
m += a[i];
}
f[0][0][0] = 1;
for (int i = 0; i < n; i ++)
{
for (int j = 0; j <= m / 2; j ++)
{
for (int k = 0; k <= m / 2; k ++)
{
if (f[i][j][k])
{
f[i + 1][j + a[i + 1]][k] = 1;
f[i + 1][j][k + a[i + 1]] = 1;
f[i + 1][j][k] = 1;
}
}
}
}
int ans = m;
for (int j = 0; j <= m; j ++)
{
for (int k = 0; k <= j; k ++)
{
if (f[n][j][k] && j >= m - j - k) ans = min(ans, j);
}
}
cout << ans << endl;
return 0;
}
倒序优化空间 \(465ms\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cassert>
using namespace std;
const int NR = 30;
const int MR = 2010;
int a[NR];
int f[MR][MR];
int main()
{
int n;
scanf("%d", &n);
int m = 0;
for (int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
m += a[i];
}
f[0][0] = 1;
for (int i = 1; i <= n; i ++)
{
for (int j = m; j >= 0; j --)
{
for (int k = m; k >= 0; k --)
{
if (j >= a[i]) f[j][k] |= f[j - a[i]][k];
if (k >= a[i]) f[j][k] |= f[j][k - a[i]];
}
}
}
int ans = m;
for (int j = 0; j <= m; j ++)
{
for (int k = 0; k <= j; k ++)
{
if (f[j][k] && j >= m - j - k) ans = min(ans, j);
}
}
cout << ans << endl;
return 0;
}
bitset 优化 \(88ms\)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <bitset>
using namespace std;
const int NR = 30;
const int MR = 2010;
int a[NR];
bitset<MR> f[MR];
int main()
{
int n;
scanf("%d", &n);
int m = 0;
for (int i = 1; i <= n; i ++)
{
scanf("%d", &a[i]);
m += a[i];
}
f[0][0] = 1;
for (int i = 1; i <= n; i ++)
{
for (int j = m; j >= 0; j --)
{
f[j] |= f[j] << a[i];
if (j >= a[i]) f[j] |= f[j - a[i]];
}
}
int ans = m;
for (int j = 0; j <= m; j ++)
{
for (int k = 0; k <= j; k ++)
{
if (f[j][k] && j >= m - j - k) ans = min(ans, j);
}
}
cout << ans << endl;
return 0;
}

浙公网安备 33010602011771号