C语言动态规划:0-1背包问题深度解析

本文献给:

想要彻底掌握0-1背包问题的C语言学习者。如果你已经了解动态规划基础,希望深入理解背包问题的精髓并掌握各种优化技巧------本文将带你从实现到优化,全面剖析0-1背包问题。

你将学到:

  1. 0-1背包问题的本质和数学建模
  2. 基础二维DP解法的实现细节
  3. 空间优化的滚动数组技巧
  4. 多种边界处理和初始化方法
  5. 背包问题的变体和实际应用
  6. 分析算法复杂度和进行性能优化

让我们深入探索0-1背包问题的完整解决方案!


目录

第一部分:问题本质与数学建模

1. 0-1背包问题定义

给定一组物品,每个物品有重量和价值,在不超过背包容量的前提下,如何选择物品使得总价值最大。

问题形式化描述:

  • 输入:n个物品,背包容量C
  • 每个物品i:重量 w i w_i wi,价值 v i v_i vi
  • 输出:选择物品的子集S,使得 ∑ i ∈ S w i ≤ C ∑{i∈S} w_i ≤ C ∑i∈Swi≤C,且 ∑ i ∈ S v i ∑{i∈S} v_i ∑i∈Svi最大

"0-1"的含义:

每个物品要么完整选取(1),要么不选(0),不能分割,即不存在中间值。

c 复制代码
// 问题数据结构定义
typedef struct {
    int weight;   // 物品重量
    int value;    // 物品价值
    int selected; // 是否被选择(用于回溯)
} Item;

// 问题实例示例
Item items[] = {
    {2, 3, 0},  // 物品0:重量2,价值3
    {3, 4, 0},  // 物品1:重量3,价值4  
    {4, 5, 0},  // 物品2:重量4,价值5
    {5, 6, 0}   // 物品3:重量5,价值6
};
int n = 4;      // 物品数量
int capacity = 8; // 背包容量

2. 问题的数学表达

目标函数:
∑ i = 1 n v i × x i \sum_{i=1}^{n} v_i \times x_i i=1∑nvi×xi

约束条件:
∑ i = 1 n w i × x i ≤ C x i ∈ { 0 , 1 } , i = 1 , 2 , ... , n \begin{align} & \sum_{i=1}^{n} w_i \times x_i \leq C \\ & x_i \in \{0, 1\}, \quad i = 1, 2, \dots, n \end{align} i=1∑nwi×xi≤Cxi∈{0,1},i=1,2,...,n

c 复制代码
// 数学模型的C语言表示
int objective_function(Item items[], int n, int solution[]) {
    int total_value = 0;
    for (int i = 0; i < n; i++) {
        if (solution[i] == 1) {
            total_value += items[i].value;
        }
    }
    return total_value;
}

int constraint_check(Item items[], int n, int solution[], int capacity) {
    int total_weight = 0;
    for (int i = 0; i < n; i++) {
        if (solution[i] == 1) {
            total_weight += items[i].weight;
        }
    }
    return total_weight <= capacity;
}

3. 问题的计算复杂性

0-1背包问题是NP完全问题,但可以通过动态规划在伪多项式时间内求解。

第二部分:基础动态规划解法

1. 状态定义与转移方程

状态定义:
dp[i][w] 表示考虑前i个物品,背包容量为w时的最大价值。

状态转移方程:
d p [ i ] [ w ] = { max ⁡ ( d p [ i − 1 ] [ w ] , d p [ i − 1 ] [ w − w i ] + v i ) if w ≥ w i d p [ i − 1 ] [ w ] if w < w i dp[i][w] = \begin{cases} \max(dp[i-1][w],\ dp[i-1][w - w_i] + v_i) & \text{if } w \geq w_i \\ dp[i-1][w] & \text{if } w < w_i \end{cases} dp[i][w]={max(dp[i−1][w], dp[i−1][w−wi]+vi)dp[i−1][w]if w≥wiif w<wi

解释:

  • 第一种情况w ≥ w_i):可以选择放入或不放入第 i 个物品,取两者中的最大值
  • 第二种情况w < w_i):当前背包容量不足以放入第 i 个物品,只能选择不放入

边界条件:

  • 当没有物品时 :对于所有背包容量 w ≥ 0 w \geq 0 w≥0,有 d p [ 0 ] [ w ] = 0 dp[0][w] = 0 dp[0][w]=0
  • 当背包容量为0时 :对于所有物品数量 i ≥ 0 i \geq 0 i≥0,有 d p [ i ] [ 0 ] = 0 dp[i][0] = 0 dp[i][0]=0
c 复制代码
// 状态转移的可视化
void print_state_transition(int i, int w, Item items[]) {
    printf("计算 dp[%d][%d]:\n", i, w);
    
    if (w < items[i-1].weight) {
        printf("  物品%d重量%d > 当前容量%d,不能选择\n", i, items[i-1].weight, w);
        printf("  继承 dp[%d][%d] = %d\n", i-1, w, -1); // 实际值需要计算
    } else {
        printf("  选择1: 不选物品%d → dp[%d][%d]\n", i, i-1, w);
        printf("  选择2: 选择物品%d → dp[%d][%d] + %d\n", 
               i, i-1, w - items[i-1].weight, items[i-1].value);
        printf("  取最大值\n");
    }
}

2. 完整的二维DP实现

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 完整的二维DP解法
int knapsack_2d(Item items[], int n, int capacity) {
    // 创建DP表 (n+1) x (capacity+1)
    int** dp = (int**)malloc((n + 1) * sizeof(int*));
    for (int i = 0; i <= n; i++) {
        dp[i] = (int*)malloc((capacity + 1) * sizeof(int));
    }
    
    // 初始化边界条件
    for (int i = 0; i <= n; i++) {
        dp[i][0] = 0;  // 容量为0时,价值为0
    }
    for (int w = 0; w <= capacity; w++) {
        dp[0][w] = 0;  // 没有物品时,价值为0
    }
    
    // 填充DP表
    for (int i = 1; i <= n; i++) {
        for (int w = 1; w <= capacity; w++) {
            if (items[i-1].weight <= w) {
                // 可以选择当前物品
                int include = items[i-1].value + dp[i-1][w - items[i-1].weight];
                int exclude = dp[i-1][w];
                dp[i][w] = (include > exclude) ? include : exclude;
            } else {
                // 不能选择当前物品
                dp[i][w] = dp[i-1][w];
            }
        }
    }
    
    int result = dp[n][capacity];
    
    // 释放内存
    for (int i = 0; i <= n; i++) {
        free(dp[i]);
    }
    free(dp);
    
    return result;
}

3. 回溯求解具体方案

c 复制代码
// 回溯找出具体选择的物品
void find_selected_items(Item items[], int n, int capacity, int** dp, int selected[]) {
    int w = capacity;
    
    for (int i = n; i > 0; i--) {
        if (dp[i][w] != dp[i-1][w]) {
            // 选择了物品i-1
            selected[i-1] = 1;
            w -= items[i-1].weight;
        } else {
            selected[i-1] = 0;
        }
    }
}

// 完整的带回溯的解法
int knapsack_with_solution(Item items[], int n, int capacity, int selected[]) {
    // 创建DP表
    int** dp = (int**)malloc((n + 1) * sizeof(int*));
    for (int i = 0; i <= n; i++) {
        dp[i] = (int*)malloc((capacity + 1) * sizeof(int));
    }
    
    // 初始化
    for (int i = 0; i <= n; i++) {
        dp[i][0] = 0;
    }
    for (int w = 0; w <= capacity; w++) {
        dp[0][w] = 0;
    }
    
    // 填充DP表
    for (int i = 1; i <= n; i++) {
        for (int w = 1; w <= capacity; w++) {
            if (items[i-1].weight <= w) {
                int include = items[i-1].value + dp[i-1][w - items[i-1].weight];
                int exclude = dp[i-1][w];
                dp[i][w] = (include > exclude) ? include : exclude;
            } else {
                dp[i][w] = dp[i-1][w];
            }
        }
    }
    
    // 回溯找出具体方案
    find_selected_items(items, n, capacity, dp, selected);
    
    int result = dp[n][capacity];
    
    // 释放内存
    for (int i = 0; i <= n; i++) {
        free(dp[i]);
    }
    free(dp);
    
    return result;
}

// 打印解决方案
void print_solution(Item items[], int n, int selected[], int max_value) {
    printf("最优解: 总价值 = %d\n", max_value);
    printf("选择的物品: ");
    
    int total_weight = 0;
    for (int i = 0; i < n; i++) {
        if (selected[i]) {
            printf("物品%d(重量:%d,价值:%d) ", i, items[i].weight, items[i].value);
            total_weight += items[i].weight;
        }
    }
    printf("\n总重量: %d\n", total_weight);
}

4. DP表可视化

c 复制代码
// 打印DP表(用于调试和理解)
void print_dp_table(int** dp, int n, int capacity, Item items[]) {
    printf("\nDP表:\n");
    printf("      ");
    for (int w = 0; w <= capacity; w++) {
        printf("%3d ", w);
    }
    printf("\n");
    
    for (int i = 0; i <= n; i++) {
        if (i == 0) {
            printf("初始 ");
        } else {
            printf("物品%d ", i);
        }
        
        for (int w = 0; w <= capacity; w++) {
            printf("%3d ", dp[i][w]);
        }
        printf("\n");
    }
}

// 带可视化的完整解法
int knapsack_visualized(Item items[], int n, int capacity) {
    int** dp = (int**)malloc((n + 1) * sizeof(int*));
    for (int i = 0; i <= n; i++) {
        dp[i] = (int*)malloc((capacity + 1) * sizeof(int));
    }
    
    // 初始化
    for (int i = 0; i <= n; i++) dp[i][0] = 0;
    for (int w = 0; w <= capacity; w++) dp[0][w] = 0;
    
    printf("开始填充DP表...\n");
    
    for (int i = 1; i <= n; i++) {
        printf("\n--- 处理物品%d (重量:%d, 价值:%d) ---\n", 
               i, items[i-1].weight, items[i-1].value);
        
        for (int w = 1; w <= capacity; w++) {
            if (items[i-1].weight <= w) {
                int include = items[i-1].value + dp[i-1][w - items[i-1].weight];
                int exclude = dp[i-1][w];
                dp[i][w] = (include > exclude) ? include : exclude;
                
                if (include > exclude) {
                    printf("容量%2d: 选择物品%d → %d + dp[%d][%d] = %d\n", 
                           w, i, items[i-1].value, i-1, w - items[i-1].weight, include);
                } else {
                    printf("容量%2d: 不选物品%d → dp[%d][%d] = %d\n", 
                           w, i, i-1, w, exclude);
                }
            } else {
                dp[i][w] = dp[i-1][w];
                printf("容量%2d: 无法选择物品%d → dp[%d][%d] = %d\n", 
                       w, i, i-1, w, dp[i][w]);
            }
        }
    }
    
    print_dp_table(dp, n, capacity, items);
    
    int result = dp[n][capacity];
    
    for (int i = 0; i <= n; i++) free(dp[i]);
    free(dp);
    
    return result;
}

第三部分:空间优化技巧

1. 滚动数组优化

使用一维数组代替二维数组,将空间复杂度从O(n×C)降到O©。

c 复制代码
// 基础的空间优化版本
int knapsack_1d(Item items[], int n, int capacity) {
    // 只使用一维数组
    int* dp = (int*)malloc((capacity + 1) * sizeof(int));
    
    // 初始化:没有物品时,所有容量价值为0
    for (int w = 0; w <= capacity; w++) {
        dp[w] = 0;
    }
    
    // 处理每个物品
    for (int i = 0; i < n; i++) {
        // 必须从后向前遍历,避免重复选择
        for (int w = capacity; w >= items[i].weight; w--) {
            if (dp[w] < dp[w - items[i].weight] + items[i].value) {
                dp[w] = dp[w - items[i].weight] + items[i].value;
            }
        }
    }
    
    int result = dp[capacity];
    free(dp);
    return result;
}

2. 反向遍历的原理

理解为什么必须从后向前遍历:

c 复制代码
// 演示错误的正向遍历
int knapsack_wrong_direction(Item items[], int n, int capacity) {
    int* dp = (int*)malloc((capacity + 1) * sizeof(int));
    
    for (int w = 0; w <= capacity; w++) {
        dp[w] = 0;
    }
    
    printf("错误的向前遍历演示:\n");
    for (int i = 0; i < n; i++) {
        printf("处理物品%d (重量:%d, 价值:%d)\n", i, items[i].weight, items[i].value);
        
        // 错误:从前向后遍历
        for (int w = items[i].weight; w <= capacity; w++) {
            int new_value = dp[w - items[i].weight] + items[i].value;
            if (new_value > dp[w]) {
                printf("  容量%d: 更新 %d -> %d\n", w, dp[w], new_value);
                dp[w] = new_value;
            }
        }
        
        // 打印当前状态
        printf("  当前DP数组: ");
        for (int w = 0; w <= capacity; w++) {
            printf("%d ", dp[w]);
        }
        printf("\n");
    }
    
    int result = dp[capacity];
    free(dp);
    return result;
}

// 对比正确和错误的遍历
void compare_traversal_directions(Item items[], int n, int capacity) {
    printf("=== 遍历方向对比 ===\n");
    
    int correct = knapsack_1d(items, n, capacity);
    printf("正确解法(反向遍历)结果: %d\n", correct);
    
    int wrong = knapsack_wrong_direction(items, n, capacity);
    printf("错误解法(正向遍历)结果: %d\n", wrong);
    
    if (wrong > correct) {
        printf("警告:正向遍历导致物品被多次选择!\n");
    }
}

3. 带方案回溯的优化版本

c 复制代码
// 空间优化且能回溯具体方案的解法
int knapsack_1d_with_solution(Item items[], int n, int capacity, int selected[]) {
    int* dp = (int*)malloc((capacity + 1) * sizeof(int));
    int** decision = (int**)malloc((n) * sizeof(int*));
    
    // 初始化决策记录数组
    for (int i = 0; i < n; i++) {
        decision[i] = (int*)malloc((capacity + 1) * sizeof(int));
        for (int w = 0; w <= capacity; w++) {
            decision[i][w] = 0;  // 0表示不选,1表示选
        }
    }
    
    // 初始化DP数组
    for (int w = 0; w <= capacity; w++) {
        dp[w] = 0;
    }
    
    // 填充DP表和决策表
    for (int i = 0; i < n; i++) {
        for (int w = capacity; w >= items[i].weight; w--) {
            if (dp[w] < dp[w - items[i].weight] + items[i].value) {
                dp[w] = dp[w - items[i].weight] + items[i].value;
                decision[i][w] = 1;  // 记录选择决策
            }
        }
    }
    
    // 回溯找出具体方案
    int w = capacity;
    for (int i = n - 1; i >= 0; i--) {
        if (decision[i][w] == 1) {
            selected[i] = 1;
            w -= items[i].weight;
        } else {
            selected[i] = 0;
        }
    }
    
    int result = dp[capacity];
    
    // 释放内存
    free(dp);
    for (int i = 0; i < n; i++) {
        free(decision[i]);
    }
    free(decision);
    
    return result;
}

4. 进一步的空间优化技巧

c 复制代码
// 使用位运算优化边界检查
int knapsack_optimized(Item items[], int n, int capacity) {
    // 只分配需要的空间
    int* dp = (int*)calloc(capacity + 1, sizeof(int));
    
    for (int i = 0; i < n; i++) {
        int weight = items[i].weight;
        int value = items[i].value;
        
        // 使用单个变量避免重复计算
        int min_w = weight;
        
        // 手动循环展开优化
        int w = capacity;
        while (w >= min_w) {
            int new_val = dp[w - weight] + value;
            if (new_val > dp[w]) {
                dp[w] = new_val;
            }
            w--;
        }
    }
    
    int result = dp[capacity];
    free(dp);
    return result;
}

// 针对小价值的优化(使用价值作为维度)
int knapsack_value_based(Item items[], int n, int capacity) {
    // 计算总价值
    int total_value = 0;
    for (int i = 0; i < n; i++) {
        total_value += items[i].value;
    }
    
    // dp[v] 表示获得价值v所需的最小重量
    int* dp = (int*)malloc((total_value + 1) * sizeof(int));
    
    // 初始化
    for (int v = 1; v <= total_value; v++) {
        dp[v] = capacity + 1;  // 初始化为大于背包容量的值
    }
    dp[0] = 0;
    
    // 填充DP表
    for (int i = 0; i < n; i++) {
        for (int v = total_value; v >= items[i].value; v--) {
            if (dp[v] > dp[v - items[i].value] + items[i].weight) {
                dp[v] = dp[v - items[i].value] + items[i].weight;
            }
        }
    }
    
    // 找到不超过背包容量的最大价值
    int result = 0;
    for (int v = total_value; v >= 0; v--) {
        if (dp[v] <= capacity) {
            result = v;
            break;
        }
    }
    
    free(dp);
    return result;
}

第四部分:边界处理与初始化

1. 各种初始化策略

c 复制代码
// 不同的初始化方法对比
typedef enum {
    INIT_ZERO,          // 全部初始化为0
    INIT_NEGATIVE_INF,  // 负无穷,用于必须装满的情况
    INIT_CUSTOM         // 自定义初始化
} InitStrategy;

int knapsack_with_init(Item items[], int n, int capacity, InitStrategy strategy) {
    int* dp = (int*)malloc((capacity + 1) * sizeof(int));
    
    // 根据策略初始化
    switch (strategy) {
        case INIT_ZERO:
            for (int w = 0; w <= capacity; w++) {
                dp[w] = 0;
            }
            break;
            
        case INIT_NEGATIVE_INF:
            dp[0] = 0;
            for (int w = 1; w <= capacity; w++) {
                dp[w] = -1;  // 用-1表示负无穷
            }
            break;
            
        case INIT_CUSTOM:
            // 自定义初始化逻辑
            for (int w = 0; w <= capacity; w++) {
                dp[w] = (w >= items[0].weight) ? items[0].value : 0;
            }
            break;
    }
    
    // 处理剩余物品
    int start_i = (strategy == INIT_CUSTOM) ? 1 : 0;
    for (int i = start_i; i < n; i++) {
        for (int w = capacity; w >= items[i].weight; w--) {
            if (strategy == INIT_NEGATIVE_INF) {
                // 必须装满的处理
                if (dp[w - items[i].weight] != -1) {
                    int new_val = dp[w - items[i].weight] + items[i].value;
                    if (new_val > dp[w] || dp[w] == -1) {
                        dp[w] = new_val;
                    }
                }
            } else {
                // 普通处理
                if (dp[w] < dp[w - items[i].weight] + items[i].value) {
                    dp[w] = dp[w - items[i].weight] + items[i].value;
                }
            }
        }
    }
    
    int result = (strategy == INIT_NEGATIVE_INF && dp[capacity] == -1) ? 0 : dp[capacity];
    free(dp);
    return result;
}

2. 必须装满的背包问题

c 复制代码
// 背包必须装满的解法
int knapsack_must_fill(Item items[], int n, int capacity) {
    int* dp = (int*)malloc((capacity + 1) * sizeof(int));
    
    // 初始化:只有容量0可以装满(价值0),其他容量初始为负无穷
    dp[0] = 0;
    for (int w = 1; w <= capacity; w++) {
        dp[w] = -1;  // -1表示无法装满
    }
    
    for (int i = 0; i < n; i++) {
        for (int w = capacity; w >= items[i].weight; w--) {
            if (dp[w - items[i].weight] != -1) {
                int new_val = dp[w - items[i].weight] + items[i].value;
                if (new_val > dp[w] || dp[w] == -1) {
                    dp[w] = new_val;
                }
            }
        }
    }
    
    int result = (dp[capacity] == -1) ? 0 : dp[capacity];
    free(dp);
    return result;
}

3. 输入验证和错误处理

c 复制代码
// 完整的输入验证
int validate_knapsack_input(Item items[], int n, int capacity) {
    if (n <= 0) {
        printf("错误:物品数量必须大于0\n");
        return 0;
    }
    
    if (capacity <= 0) {
        printf("错误:背包容量必须大于0\n");
        return 0;
    }
    
    for (int i = 0; i < n; i++) {
        if (items[i].weight <= 0) {
            printf("错误:物品%d的重量必须大于0\n", i);
            return 0;
        }
        if (items[i].value < 0) {
            printf("错误:物品%d的价值不能为负数\n", i);
            return 0;
        }
        if (items[i].weight > capacity) {
            printf("警告:物品%d的重量%d超过背包容量%d\n", 
                   i, items[i].weight, capacity);
        }
    }
    
    return 1;
}

// 安全的背包求解函数
int safe_knapsack(Item items[], int n, int capacity, int* error_code) {
    *error_code = 0;
    
    // 输入验证
    if (!validate_knapsack_input(items, n, capacity)) {
        *error_code = 1;
        return 0;
    }
    
    // 检查是否需要预处理(过滤明显无用的物品)
    Item* filtered_items = preprocess_items(items, n, capacity, &n);
    
    // 选择适当的算法
    int result;
    if (capacity > 1000000) {
        // 容量太大,使用价值基础的DP
        result = knapsack_value_based(filtered_items, n, capacity);
    } else if (n * capacity > 10000000) {
        // 状态数太多,使用近似算法
        result = knapsack_greedy(filtered_items, n, capacity);
    } else {
        // 使用标准DP
        result = knapsack_1d(filtered_items, n, capacity);
    }
    
    if (filtered_items != items) {
        free(filtered_items);
    }
    
    return result;
}

// 物品预处理:移除明显无用的物品
Item* preprocess_items(Item items[], int n, int capacity, int* new_n) {
    // 统计有用物品
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (items[i].weight <= capacity && items[i].value > 0) {
            count++;
        }
    }
    
    if (count == n) {
        *new_n = n;
        return items;  // 无需处理
    }
    
    // 创建新的物品数组
    Item* filtered = (Item*)malloc(count * sizeof(Item));
    int idx = 0;
    for (int i = 0; i < n; i++) {
        if (items[i].weight <= capacity && items[i].value > 0) {
            filtered[idx++] = items[i];
        }
    }
    
    *new_n = count;
    return filtered;
}

第五部分:总结与最佳实践

核心要点总结

算法选择指南:

  • 小规模问题:二维DP(便于理解和调试)
  • 中等规模:一维DP(空间优化)
  • 大规模问题:价值基础DP或启发式算法
  • 必须装满:特殊初始化处理

关键优化技巧:

  • 反向遍历避免重复选择
  • 滚动数组降低空间复杂度
  • 物品预处理过滤无用物品
  • 根据问题特点选择合适维度

常见陷阱:

  • 正向遍历导致物品重复选择
  • 忘记初始化边界条件
  • 容量或价值为0的特殊情况
  • 内存分配失败处理

第六部分:常见问题解答

Q1:为什么0-1背包问题使用动态规划而不是贪心算法?

A1:贪心算法(按价值密度排序)不能保证最优解。反例:容量为5,物品1(4,5)价值密度1.25,物品2(3,4)价值密度1.33,物品3(2,2)价值密度1.0。贪心会选择物品2+物品3=价值6,但最优解是物品1+物品3=价值7。

Q2:什么情况下0-1背包问题可以用贪心算法得到最优解?

A2:当所有物品的重量都能整除背包容量,且按价值密度排序后,贪心选择能得到最优解。但在一般情况下,贪心只能得到近似解。

Q3:如何选择二维DP还是一维DP?

A3:二维DP更直观,容易理解和调试,适合学习和小规模问题。一维DP空间效率高,适合生产环境和大规模问题。如果需要回溯具体方案,二维DP更容易实现。

Q4:背包容量很大时怎么办?

A4:当容量C很大时(比如10^9),但物品价值总和不大时,可以使用价值基础的DP,将状态定义为达到某个价值所需的最小重量,时间复杂度O(n×∑v_i)。

Q5:如何判断DP数组需要多大的空间?

A5:需要(n+1)×(C+1)的空间用于二维DP,或者(C+1)的空间用于一维DP。在分配内存前应该检查n×C是否在可接受范围内,避免内存溢出。


觉得文章有帮助?别忘了:

👍 点赞 👍 - 给我一点鼓励
⭐ 收藏 ⭐ - 方便以后查看
🔔 关注 🔔 - 获取更新通知

标签: #C语言算法 #动态规划 #0-1背包问题 #算法优化

相关推荐
Want5951 小时前
C/C++跳动的爱心③
java·c语言·c++
量子炒饭大师1 小时前
Cyber骇客的数据链路重构 ——【初阶数据结构与算法】线性表之单链表
c语言·数据结构·c++·windows·git·链表·github
弱冠少年1 小时前
xiaozhi任务管理分析(基于ESP32)
c语言
stay night481 小时前
F4 状态机模型
c语言
GUET_一路向前1 小时前
【C语言无符号常量好处】`4U` 表示一个无符号整数常量 4
c语言·开发语言·无符号常量
玖剹2 小时前
floodfill算法题目(二)
c语言·c++·算法·leetcode·深度优先·dfs·深度优先遍历
surtr12 小时前
区间查询mex异或gcd (焰与霜的共鸣,可持久化线段树+思维)
数据结构·c++·算法·数学建模·stl·动态规划
自然常数e2 小时前
深入理解指针(3)
c语言·visual studio