3.30

四、背包 DP 模板
背包最核心区别
• 01 背包:每个物品最多选一次,容量 j 倒序。
• 完全背包:每个物品可以选无限次,容量 j 正序。
• 方案数问题:通常 dp[0] = 1,转移用加法。

  1. 01 背包
    题型特征:每个物品只能选 0 次或 1 次,有容量限制,求最大价值。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, V;
    cin >> n >> V;
    vector<int> dp(V + 1, 0);

    for (int i = 1; i <= n; i++) {
        int w, val;
        cin >> w >> val;

        for (int j = V; j >= w; j--) {
            dp[j] = max(dp[j], dp[j - w] + val);
        }
    }

    cout << dp[V] << '\n';
    return 0;
}
  1. 完全背包
    题型特征:每个物品可以选无限次,有容量限制,求最大价值。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, V;
    cin >> n >> V;
    vector<int> dp(V + 1, 0);

    for (int i = 1; i <= n; i++) {
        int w, val;
        cin >> w >> val;

        for (int j = w; j <= V; j++) {
            dp[j] = max(dp[j], dp[j - w] + val);
        }
    }

    cout << dp[V] << '\n';
    return 0;
}
  1. 多重背包:二进制优化
    题型特征:每种物品最多有 cnt 个。常用二进制拆分,把多重背包转成 01 背包。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, V;
    cin >> n >> V;
    vector<int> dp(V + 1, 0);

    for (int i = 1; i <= n; i++) {
        int w, val, cnt;
        cin >> w >> val >> cnt;

        int k = 1;
        while (cnt > 0) {
            int num = min(k, cnt);
            int weight = num * w;
            int value = num * val;

            for (int j = V; j >= weight; j--) {
                dp[j] = max(dp[j], dp[j - weight] + value);
            }

            cnt -= num;
            k *= 2;
        }
    }

    cout << dp[V] << '\n';
    return 0;
}
  1. 分组背包
    题型特征:物品被分成若干组,每组最多选一个。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int g, V;
    cin >> g >> V;
    vector<int> dp(V + 1, 0);

    for (int i = 1; i <= g; i++) {
        int cnt;
        cin >> cnt;
        vector<int> w(cnt + 1), val(cnt + 1);
        for (int k = 1; k <= cnt; k++) cin >> w[k] >> val[k];

        for (int j = V; j >= 0; j--) {
            for (int k = 1; k <= cnt; k++) {
                if (j >= w[k]) {
                    dp[j] = max(dp[j], dp[j - w[k]] + val[k]);
                }
            }
        }
    }

    cout << dp[V] << '\n';
    return 0;
}
  1. 恰好装满背包
    题型特征:要求容量必须刚好等于 V,不是“不超过 V”。初始化和普通背包不同。
#include <bits/stdc++.h>
using namespace std;

const int INF = 1e9;

int main() {
    int n, V;
    cin >> n >> V;

    vector<int> dp(V + 1, -INF);
    dp[0] = 0;

    for (int i = 1; i <= n; i++) {
        int w, val;
        cin >> w >> val;

        for (int j = V; j >= w; j--) {
            dp[j] = max(dp[j], dp[j - w] + val);
        }
    }

    if (dp[V] < 0) cout << "impossible\n";
    else cout << dp[V] << '\n';

    return 0;
}
  1. 背包求方案数
    题型特征:问有多少种方法凑出容量 V,不是最大价值。
// 01 背包方案数:每个数只能用一次
vector<long long> dp(V + 1, 0);
dp[0] = 1;

for (int i = 1; i <= n; i++) {
    int w;
    cin >> w;
    for (int j = V; j >= w; j--) {
        dp[j] += dp[j - w];
    }
}
// 完全背包方案数:每个数可以无限次使用
vector<long long> dp(V + 1, 0);
dp[0] = 1;

for (int i = 1; i <= n; i++) {
    int w;
    cin >> w;
    for (int j = w; j <= V; j++) {
        dp[j] += dp[j - w];
    }
}
  1. 二维费用背包
    题型特征:有两个限制条件,例如体积和重量、时间和金钱。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, V, M;
    cin >> n >> V >> M;
    vector<vector<int>> dp(V + 1, vector<int>(M + 1, 0));

    for (int i = 1; i <= n; i++) {
        int v, m, val;
        cin >> v >> m >> val;

        for (int j = V; j >= v; j--) {
            for (int k = M; k >= m; k--) {
                dp[j][k] = max(dp[j][k], dp[j - v][k - m] + val);
            }
        }
    }

    cout << dp[V][M] << '\n';
    return 0;
}

五、路径 DP

  1. 网格最小路径和
    题型特征:从左上角走到右下角,每次只能向右或向下,求最小路径和。
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<int>> a(n + 1, vector<int>(m + 1));
    vector<vector<int>> dp(n + 1, vector<int>(m + 1, 1e9));

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) cin >> a[i][j];
    }

    dp[1][1] = a[1][1];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (i == 1 && j == 1) continue;
            if (i > 1) dp[i][j] = min(dp[i][j], dp[i - 1][j] + a[i][j]);
            if (j > 1) dp[i][j] = min(dp[i][j], dp[i][j - 1] + a[i][j]);
        }
    }

    cout << dp[n][m] << '\n';
    return 0;
}
  1. 网格路径方案数
#include <bits/stdc++.h>
using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    vector<vector<long long>> dp(n + 1, vector<long long>(m + 1, 0));

    dp[1][1] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (i == 1 && j == 1) continue;
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }

    cout << dp[n][m] << '\n';
    return 0;
}

六、股票 DP

1. 只能买卖一次
int minPrice = price[0];
int ans = 0;

for (int i = 1; i < n; i++) {
    ans = max(ans, price[i] - minPrice);
    minPrice = min(minPrice, price[i]);
}

2. 可以买卖无限次
int ans = 0;
for (int i = 1; i < n; i++) {
    if (price[i] > price[i - 1]) {
        ans += price[i] - price[i - 1];
    }
}

3. 最多买卖 K 次
vector<int> buy(K + 1, -1e9);
vector<int> sell(K + 1, 0);

for (int p : price) {
    for (int j = 1; j <= K; j++) {
        buy[j] = max(buy[j], sell[j - 1] - p);
        sell[j] = max(sell[j], buy[j] + p);
    }
}

cout << sell[K] << '\n';

七、比赛易错点清单
易错点 正确做法
01 背包循环方向写反 容量 j 从 V 到 w 倒序遍历
完全背包循环方向写反 容量 j 从 w 到 V 正序遍历
恰好装满初始化错误 dp[0]=0,其余设为 -INF 或 INF
方案数忘记 dp[0]=1 什么都不选凑出 0 是 1 种方案
答案太大溢出 优先用 long long,题目要求时取模
最大值初始值过小/过大错误 最大值用 -INF,最小值用 INF
数组下标混乱 字符串一般 s[i-1] 对应 dp 第 i 位
n=1 边界漏掉 打家劫舍、爬楼梯、股票题要特判

八、赛前最推荐背熟的 8 个模板

  1. 01 背包
  2. 完全背包
  3. 多重背包二进制优化
  4. LIS:O(n²) 和 O(n log n)
  5. LCS
  6. 最大子段和
  7. 打家劫舍
  8. 网格路径 DP
    九、万能 C++ 开头模板
#include <bits/stdc++.h>
using namespace std;

using ll = long long;

const int INF = 1e9;
const ll LINF = 4e18;
const int MOD = 1e9 + 7;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    return 0;

1.image

怎么将浮点数转换为字符串
输入:直接把sring类型改为double 输入就好了
或加‘0’

posted @ 2026-03-30 09:08  ervgiu28hg34y827  阅读(16)  评论(0)    收藏  举报