Files
algo2025/dynamic/knapsack.cpp
2025-12-18 16:00:22 +08:00

160 lines
5.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <chrono>
#include <random>
struct Item {
int weight; // 物品重量
int value; // 物品价值
int count; // 物品数量(用于多重背包问题)
};
// 全局操作计数器,用于性能分析
long long ops_count = 0;
// =================================================================
// 完全背包问题算法实现
// =================================================================
// 算法1朴素动态规划三层循环
int complete_knapsack_v1(const std::vector<Item>& items, int capacity) {
ops_count = 0; // 重置操作计数器
int n = items.size();
if (n == 0) return 0;
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
// 遍历每个物品
for (int i = 1; i <= n; ++i) {
int w = items[i - 1].weight;
int v = items[i - 1].value;
// 遍历每个容量
for (int j = 0; j <= capacity; ++j) {
dp[i][j] = dp[i-1][j]; // 不选择第i个物品
ops_count++;
// 尝试选择k个第i个物品
for (int k = 1; k * w <= j; ++k) {
ops_count++;
if (dp[i-1][j - k * w] + k * v > dp[i][j]) {
dp[i][j] = dp[i-1][j - k * w] + k * v;
}
}
}
}
return dp[n][capacity];
}
// 算法2优化的二维动态规划两层循环
int complete_knapsack_v2(const std::vector<Item>& items, int capacity) {
ops_count = 0; // 重置操作计数器
int n = items.size();
if (n == 0) return 0;
std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1, 0));
// 遍历每个物品
for (int i = 1; i <= n; ++i) {
int w = items[i - 1].weight;
int v = items[i - 1].value;
// 遍历每个容量
for (int j = 0; j <= capacity; ++j) {
ops_count++;
if (j < w) {
dp[i][j] = dp[i - 1][j]; // 容量不足不能选择第i个物品
} else {
// 选择不选第i个物品或选择第i个物品的最大值
dp[i][j] = std::max(dp[i - 1][j], dp[i][j - w] + v);
}
}
}
return dp[n][capacity];
}
// 算法3空间优化的动态规划一维数组
int complete_knapsack_v3(const std::vector<Item>& items, int capacity) {
ops_count = 0; // 重置操作计数器
std::vector<int> dp(capacity + 1, 0); // 一维DP数组
// 遍历每个物品
for (const auto& item : items) {
// 从物品重量开始遍历容量(完全背包)
for (int j = item.weight; j <= capacity; ++j) {
ops_count++;
dp[j] = std::max(dp[j], dp[j - item.weight] + item.value);
}
}
return dp[capacity];
}
// =================================================================
// 基准测试运行器
// =================================================================
void run_experiments(int min_n, int max_n, int step_n, int trials, int capacity) {
// 初始化随机数生成器
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> weight_dist(1, 40); // 重量范围1-40
std::uniform_int_distribution<> value_dist(1, 100); // 价值范围1-100
// 输出CSV格式的表头
std::cout << "n,algo,time_us,ops\n";
// 对不同物品数量进行测试
for (int n = min_n; n <= max_n; n += step_n) {
if (n==0) continue;
long long total_time_v1 = 0, total_ops_v1 = 0;
long long total_time_v2 = 0, total_ops_v2 = 0;
long long total_time_v3 = 0, total_ops_v3 = 0;
// 进行多次试验取平均值
for (int t = 0; t < trials; ++t) {
// 生成随机物品
std::vector<Item> items(n);
for (int i = 0; i < n; ++i) {
items[i] = {weight_dist(gen), value_dist(gen), 0};
}
// 测试算法1的运行时间
auto start_v1 = std::chrono::high_resolution_clock::now();
complete_knapsack_v1(items, capacity);
auto end_v1 = std::chrono::high_resolution_clock::now();
total_time_v1 += std::chrono::duration_cast<std::chrono::microseconds>(end_v1 - start_v1).count();
total_ops_v1 += ops_count;
// 测试算法2的运行时间
auto start_v2 = std::chrono::high_resolution_clock::now();
complete_knapsack_v2(items, capacity);
auto end_v2 = std::chrono::high_resolution_clock::now();
total_time_v2 += std::chrono::duration_cast<std::chrono::microseconds>(end_v2 - start_v2).count();
total_ops_v2 += ops_count;
// 测试算法3的运行时间
auto start_v3 = std::chrono::high_resolution_clock::now();
complete_knapsack_v3(items, capacity);
auto end_v3 = std::chrono::high_resolution_clock::now();
total_time_v3 += std::chrono::duration_cast<std::chrono::microseconds>(end_v3 - start_v3).count();
total_ops_v3 += ops_count;
}
// 输出每个算法的平均结果
std::cout << n << ",v1," << total_time_v1 / trials << "," << total_ops_v1 / trials << "\n";
std::cout << n << ",v2," << total_time_v2 / trials << "," << total_ops_v2 / trials << "\n";
std::cout << n << ",v3," << total_time_v3 / trials << "," << total_ops_v3 / trials << "\n";
}
}
int main(int argc, char* argv[]) {
// 实验参数:最小物品数、最大物品数、步长、每个物品数的试验次数、背包容量
// 为保持算法1的合理运行时间使用较小的容量和物品数量
int min_n = 5; // 最小物品数
int max_n = 25; // 最大物品数
int step_n = 5; // 物品数步长
int trials = 10; // 每个物品数的试验次数
int capacity = 100; // 背包容量
run_experiments(min_n, max_n, step_n, trials, capacity);
return 0;
}