DP学习笔记

NFLS - S+组专题1-DP1

\(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;
}
posted @ 2024-11-02 21:57  hsy8116  阅读(7)  评论(0)    收藏  举报