题目V^V

发布于:2025-07-14 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、回顾dp背包

1. 0-1背包问题

问题描述:给定n个物品,每个物品有重量w[i]和价值v[i],背包容量为W。每个物品只能选或不选,求最大价值。

状态转移方程
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])

C++实现

int knapsack01(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<vector<int>> dp(n+1, vector<int>(W+1, 0));
    
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= W; ++j) {
            if (j >= w[i-1]) {
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1]);
            } else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    return dp[n][W];
}

// 空间优化版(一维数组)
int knapsack01_opt(vector<int>& w, vector<int>& v, int W) {
    vector<int> dp(W+1, 0);
    for (int i = 0; i < w.size(); ++i) {
        for (int j = W; j >= w[i]; --j) { // 逆序更新
            dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
        }
    }
    return dp[W];
}

2. 完全背包问题

问题描述:与0-1背包类似,但每个物品可以选无限次。

状态转移方程
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i])

C++实现

int completeKnapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<int> dp(W+1, 0);
    
    for (int i = 0; i < n; ++i) {
        for (int j = w[i]; j <= W; ++j) { // 正序更新
            dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
        }
    }
    return dp[W];
}

3. 多重背包问题

问题描述:与0-1背包类似,但每个物品有数量限制s[i]。

解法:可以转化为0-1背包问题(二进制优化)

C++实现

int multipleKnapsack(vector<int>& w, vector<int>& v, vector<int>& s, int W) {
    vector<int> dp(W+1, 0);
    
    for (int i = 0; i < w.size(); ++i) {
        // 二进制优化拆分
        int num = min(s[i], W / w[i]);
        for (int k = 1; num > 0; k <<= 1) {
            if (k > num) k = num;
            num -= k;
            for (int j = W; j >= w[i] * k; --j) {
                dp[j] = max(dp[j], dp[j - w[i]*k] + v[i]*k);
            }
        }
    }
    return dp[W];
}

4. 分组背包问题

问题描述:物品被分为若干组,每组中只能选一个物品。

C++实现

int groupKnapsack(vector<vector<int>>& groups_w, vector<vector<int>>& groups_v, int W) {
    vector<int> dp(W+1, 0);
    
    for (int k = 0; k < groups_w.size(); ++k) { // 遍历每组
        for (int j = W; j >= 0; --j) { // 逆序
            for (int i = 0; i < groups_w[k].size(); ++i) { // 遍历组内物品
                if (j >= groups_w[k][i]) {
                    dp[j] = max(dp[j], dp[j - groups_w[k][i]] + groups_v[k][i]);
                }
            }
        }
    }
    return dp[W];
}

5. 背包问题变种

5.1 恰好装满背包

初始化时dp[0] = 0,其他为-INF(求最大价值)或INF(求最小价值)

5.2 方案数问题

状态转移改为累加方案数

int countWays(vector<int>& w, int W) {
    vector<int> dp(W+1, 0);
    dp[0] = 1;
    
    for (int i = 0; i < w.size(); ++i) {
        for (int j = W; j >= w[i]; --j) {
            dp[j] += dp[j - w[i]];
        }
    }
    return dp[W];
}

5.3 二维费用背包

增加一维状态表示第二种限制

int twoDimensionalKnapsack(vector<int>& w1, vector<int>& w2, vector<int>& v, int W1, int W2) {
    vector<vector<int>> dp(W1+1, vector<int>(W2+1, 0));
    
    for (int k = 0; k < w1.size(); ++k) {
        for (int i = W1; i >= w1[k]; --i) {
            for (int j = W2; j >= w2[k]; --j) {
                dp[i][j] = max(dp[i][j], dp[i-w1[k]][j-w2[k]] + v[k]);
            }
        }
    }
    return dp[W1][W2];
}

总结

  1. 0-1背包:物品只能选一次,内层循环逆序

  2. 完全背包:物品可选无限次,内层循环正序

  3. 多重背包:物品有数量限制,可二进制优化

  4. 分组背包:每组只能选一个物品,最内层循环组内物品

  5. 变种问题:注意初始化条件和状态转移含义的变化

掌握这些基本模型后,大多数背包问题都能通过适当变形解决。

二、题

1、P2196 [NOIP 1996 提高组] 挖地雷 - 洛谷

解题思路:建图,dp[i]表示为以i结尾的最大的地雷数,于是就有了

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define endl "\n"
const int MOD = 1e9 + 7;
using namespace std;

int main() {

    int n; cin >> n;
    vector<int>a(n + 1);
    vector<int>dp(21, 0); // 表示以i点结束的最大结果
    vector<vector<int>>graph(n + 1);
    vector<vector<int>>way(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    int temp;
    for (int i = 1; i < n; i++) {
        for (int j = i + 1; j <= n; j++) {
            cin >> temp;
            if (temp) {
                graph[i].push_back(j);
            }
        }
    }

    for (int i = 1; i <= n; i++)
        dp[i] = a[i], way[i].push_back(i);


    for (int i = 1; i <= n; i++) {
        for (auto j : graph[i]) {
            if (dp[j] < dp[i] + a[j]) {
                dp[j] = dp[i] + a[j];
                way[j] = way[i]; way[j].push_back(j);
            }
        }
    }

    int mx = 1;
    for (int i = 1; i <= n; i++) {
        mx = dp[mx] > dp[i] ? mx : i;
    }
    for (auto i : way[mx])
        cout << i << " ";
    cout << endl << dp[mx];
    return 0;
}

2、P4017 最大食物链计数 - 洛谷

解题思路:这个题目,我用找规律写的,如果一个物种,没用吃的,那就是生产者,如果一个物种没有被吃的物种,就是顶级物种,然后对生产者初始化为1,dp[i]+=dp[j],i是j的天敌既有了
 

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define endl "\n"
using namespace std;
const int MOD = 80112002;

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

     
    for (int i = 0; i < m; i++) {
        int a, b;
        cin >> a >> b;
        graph[a].push_back(b);
        out_degree[a]++;
        in_degree[b]++;
    }

    queue<int> q;
     
    for (int i = 1; i <= n; i++) {
        if (in_degree[i] == 0) {
            dp[i] = 1;
            q.push(i);
        }
    }

     
    while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (int v : graph[u]) {
            dp[v] = (dp[v] + dp[u]) % MOD;   
            in_degree[v]--;
            if (in_degree[v] == 0) {
                q.push(v);
            }
        }
    }

     
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (out_degree[i] == 0) {
            ans = (ans + dp[i]) % MOD;
        }
    }

    cout << ans << endl;
    return 0;
}

3、P1216 [IOI 1994] 数字三角形 Number Triangles - 洛谷

解题思路:毫无疑问dp[i][j]表示当前位置的最大值dp[i][j] = max( dp[i-1][j-1],dp[i-1][j] ) + nums[i][j]

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define endl "\n"
using namespace std;


int main() {
    ios::sync_with_stdio(0);
    cout.tie(0); cin.tie(0);
    
    int r; cin >> r;
    vector<vector<int>>nums(r + 1, vector<int>(r + 2, 0));
    vector<vector<int>>dp(r + 1, vector<int>(r + 1, 0));

    
    for (int i = 1; i <= r; i++) 
        for (int j = 1; j <= i; j++) 
            cin >> nums[i][j];
    

    int ans = 0;
    for (int i = 1; i <= r; i++) {
        for (int j = 1; j <= i; j++) {
            dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + nums[i][j];
            if (i == r)ans = max(dp[i][j], ans);
        }
    }
    cout << ans;
    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到