分支限界法 01背包c语言,算法笔记分支限界法01背包问题 代码要有注释
时间: 2024-01-18 07:02:06 浏览: 159
分别用回溯法和分支限界法求解0-1背包问题
以下是使用分支限界法解决01背包问题的C语言代码,已经添加了注释:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_N 100 // 物品数量最大值
#define MAX_W 1000 // 背包容量最大值
/* 物品结构体 */
typedef struct {
int w; // 物品重量
int v; // 物品价值
} Item;
/* 节点结构体 */
typedef struct {
int level; // 层级,表示当前处理的物品编号
int profit; // 当前已经获得的最大价值
int weight; // 当前已经使用的背包容量
} Node;
/* 优先队列结构体 */
typedef struct {
Node *data[MAX_N+1]; // 数据数组
int size; // 队列元素数量
} PriorityQueue;
/* 创建优先队列 */
PriorityQueue *create_priority_queue() {
PriorityQueue *queue = (PriorityQueue*)malloc(sizeof(PriorityQueue));
queue->size = 0;
return queue;
}
/* 销毁优先队列 */
void destroy_priority_queue(PriorityQueue *queue) {
free(queue);
}
/* 入队 */
void enqueue(PriorityQueue *queue, Node *node) {
/* 如果队列已满,则直接返回 */
if (queue->size == MAX_N) {
return;
}
/* 将新节点插入队列尾部 */
int hole = ++queue->size;
while (hole > 1 && queue->data[hole/2]->profit < node->profit) {
queue->data[hole] = queue->data[hole/2];
hole /= 2;
}
queue->data[hole] = node;
}
/* 出队 */
Node *dequeue(PriorityQueue *queue) {
/* 如果队列为空,则返回NULL */
if (queue->size == 0) {
return NULL;
}
/* 将队列头节点取出,并将队列尾节点移动到队列头 */
Node *result = queue->data[1];
Node *last = queue->data[queue->size--];
int hole = 1;
int child = 2;
while (child <= queue->size) {
if (child < queue->size && queue->data[child+1]->profit > queue->data[child]->profit) {
child++;
}
if (last->profit >= queue->data[child]->profit) {
break;
}
queue->data[hole] = queue->data[child];
hole = child;
child *= 2;
}
queue->data[hole] = last;
return result;
}
/* 求解01背包问题 */
int knapsack(int n, int w, Item *items) {
PriorityQueue *queue = create_priority_queue(); // 创建优先队列
Node *root = (Node*)malloc(sizeof(Node)); // 创建根节点
root->level = 0;
root->profit = 0;
root->weight = 0;
enqueue(queue, root); // 将根节点加入优先队列
int max_profit = 0; // 记录最大价值
while (queue->size > 0) {
Node *node = dequeue(queue); // 取出队列头节点
if (node->level >= n) { // 如果已经处理完全部物品,则更新最大价值并跳出循环
max_profit = node->profit;
break;
}
/* 处理左子节点 */
Node *left = (Node*)malloc(sizeof(Node));
left->level = node->level + 1;
left->profit = node->profit + items[left->level-1].v; // 加上当前处理的物品的价值
left->weight = node->weight + items[left->level-1].w; // 加上当前处理的物品的重量
if (left->weight <= w && left->profit > max_profit) { // 如果左子节点可以放入背包并且比当前最大价值更优,则将其加入优先队列
enqueue(queue, left);
if (left->profit > max_profit) { // 更新最大价值
max_profit = left->profit;
}
} else { // 否则,释放左子节点的内存
free(left);
}
/* 处理右子节点 */
Node *right = (Node*)malloc(sizeof(Node));
right->level = node->level + 1;
right->profit = node->profit; // 不放当前处理的物品,价值不变
right->weight = node->weight; // 不放当前处理的物品,重量不变
if (bound(right, n, w, items) > max_profit) { // 如果右子节点的上界比当前最大价值更优,则将其加入优先队列
enqueue(queue, right);
} else { // 否则,释放右子节点的内存
free(right);
}
}
destroy_priority_queue(queue); // 销毁优先队列
return max_profit; // 返回最大价值
}
/* 计算节点的上界 */
int bound(Node *node, int n, int w, Item *items) {
if (node->weight >= w) { // 如果当前节点已经超出了背包容量,则不再添加物品
return 0;
}
int bound = node->profit; // 初始上界为当前节点的价值
int i;
for (i = node->level; i < n; i++) { // 从当前节点的下一个物品开始循环
if (node->weight + items[i].w <= w) { // 如果可以放入背包,则加上物品的价值
bound += items[i].v;
node->weight += items[i].w;
} else { // 否则,加上部分价值即可
int remain = w - node->weight; // 计算剩余空间
bound += remain * (double)items[i].v / items[i].w; // 加上部分价值
break; // 跳出循环
}
}
return bound;
}
int main() {
int n, w;
printf("请输入物品数量和背包容量:\n");
scanf("%d%d", &n, &w);
Item items[MAX_N];
printf("请输入每个物品的重量和价值:\n");
int i;
for (i = 0; i < n; i++) {
scanf("%d%d", &items[i].w, &items[i].v);
}
int max_profit = knapsack(n, w, items);
printf("01背包问题的最大价值为:%d\n", max_profit);
return 0;
}
```
阅读全文