hyx0411

导航

C语言背包问题


背包问题是算法领域经典的动态规划入门问题,也是计算机考研、笔试面试的高频考点。它本质是一个资源优化问题,核心是在有限容量内,选择物品使得总价值最大。本文将带你深入理解背包问题的核心原理,用C语言实现最常见的0-1背包和完全背包,并详解代码逻辑与优化思路,零基础也能轻松看懂!

一、背包问题基础概念

背包问题主要分为三类,其中0-1背包是基础,完全背包是高频变体,多重背包是两者的延伸,我们先理清核心定义:

  1. 0-1背包:每个物品只有1件,只能选或不选,不可分割,是最基础的背包问题。

  2. 完全背包:每个物品有无限件,可重复选取,只要不超过背包容量。

  3. 多重背包:每个物品有固定数量件数,介于0-1背包和完全背包之间。

通用问题描述

给定一个容量为C的背包,有n个物品,每个物品的重量为w[i],价值为v[i]。
要求:在不超过背包容量的前提下,选出物品,使得总价值最大化。

二、0-1背包问题详解(C语言实现)

0-1背包是所有背包问题的基础,核心思想是动态规划,通过拆分子问题,逐步推导最优解。

  1. 动态规划原理

• 状态定义:定义二维数组dp[i][j],表示前i个物品,在背包容量为j时,能获得的最大价值。

• 状态转移方程:

  1. 若当前物品重量w[i] > 背包容量j:无法放入该物品,最大价值等于不放该物品的价值,即dp[i][j] = dp[i-1][j]。

  2. 若当前物品重量w[i] ≤ 背包容量j:选择放入或不放入,取价值更大的方案,即dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])。

• 边界条件:没有物品(i=0)或背包容量为0(j=0)时,最大价值都为0。

  1. 二维数组C语言实现
#include <stdio.h>
// 定义最大值,方便数组初始化
#define MAX 100

// 求两个数的最大值
int max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    // 物品数量
    int n;
    // 背包容量
    int capacity;
    printf("请输入物品数量和背包容量:");
    scanf("%d %d", &n, &capacity);

    // 存储物品重量,下标从1开始
    int w[MAX];
    // 存储物品价值,下标从1开始
    int v[MAX];
    // dp[i][j]:前i个物品,容量j的最大价值
    int dp[MAX][MAX] = {0};

    // 输入每个物品的重量和价值
    for (int i = <= n; i++) {
        printf("请输入第%d个物品的重量和价值:", i);
        scanf("%d %d", &w[i], &v[i]);
    }

    // 动态规划填充dp数组
    for (int i = 1; i<= n; i++) {
        for (int j<= capacity; j++) {
            if (w[i] > j) {
                // 物品重量大于容量,不放入
                dp[i][j] = dp[i-1][j];
            } else {
                // 选择放入或不放入,取最大值
                dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
            }
        }
    }

    // 输出最终最大价值
    printf("背包能装的最大价值为:%d\n", dp[n][capacity]);
    return 0;
}

三、完全背包问题详解(C语言实现)

完全背包和0-1背包的唯一区别:物品可重复选取,代码只需做一处修改!

  1. 核心区别

完全背包的状态转移,无需逆序遍历容量,正序遍历即可让物品重复放入背包。
状态转移方程:dp[j] = max(dp[j], dp[j - w[i]] + v[i])

  1. C语言完整代码
#include <stdio.h>
#define MAX 100

int max(int a, int b) {
    return a > b ? a : b;
}

int main() {
    int n, capacity;
    printf("请输入物品数量和背包容量:");
    scanf("%d %d", &n, &capacity);

    int w[MAX], v[MAX];
    int dp[MAX] = {0};

    for (int i = 1; i<= n; i++) {
        scanf("%d %d", &w[i], &v[i]);
    }

    // 正序遍历容量,允许重复选取物品
    for (int i = 1;<= n; i++) {
        for (int j = w[i]; j<= capacity; j++) {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    }

    printf("背包能装的最大价值为:%d\n", dp[capacity]);
    return 0;
}

四、0-1背包与完全背包核心区别

对比项 0-1背包 完全背包
物品数量 每个物品仅1件 每个物品无限件
容量遍历顺序 逆序遍历(从大到小) 正序遍历(从小到大)
核心逻辑 每个物品只选一次 物品可重复选择
空间复杂度 O(C) O(C)
时间复杂度 O(n*C) O(n*C)

五、背包问题拓展与易错点

  1. 易错点:0-1背包逆序遍历、完全背包正序遍历,千万不要搞反,否则会出现物品重复选取/无法选取的问题。

  2. 多重背包:可将有限数量的物品拆分为多个0-1背包物品,再用0-1背包代码实现。

  3. 适用场景:资源分配、预算规划、货物装载、项目投资等优化类问题。

六、总结

背包问题是动态规划的经典入门题,核心是状态定义+状态转移,掌握0-1背包的原理后,完全背包只需修改遍历顺序即可轻松实现。

先理解二维数组的动态规划逻辑,再过渡到一维数组优化,通过手动模拟dp数组填充过程,彻底吃透背包问题的核心思想。

posted on 2026-04-12 21:34  芸歆  阅读(80)  评论(1)    收藏  举报