算法设计实验四

实验四 0-1背包问题

问题描述

给定 n 个物品,物品 i 的重量是w[i], 其价值为v[i],背包容量为 c,问如何选择装入背包中的物品,使得装入背包中的物品其总价值最大?

求解方法

设 m(n, c) 表示当物品个数为 n,背包容量为 c 时的最优值,即可选的物品为0, 1, 2, ..., n-1, 则可得递归式如下:

当 w[n-1] > c 时,
    m(n, c) = m(n-1, c);
否则:
    m(n, c) = Max{ m(n-1, c), m(n-1, c - w[n-1]) + v[n-1] }
        其中,Max{a, b} 表示取 a,b 的最大值。

参考实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define N 3             // 物品个数
#define CAPACITY 10     // 最大容量限制

int weights[N]; // 重量
int prices[N];  // 价值


int maxPriceInBag(int n, int capacity) {
    if (n == 1) {
        if (weights[0] <= capacity)
            return prices[0];
        else
            return 0;
    }

    if (weights[n-1] > capacity) {
        return maxPriceInBag(n-1, capacity);
    } else {
        // 没有选择最后一个物品时的值
        int v1 = maxPriceInBag(n-1, capacity);
        // 选择最后一个物品时的值
        int v2 = maxPriceInBag(n-1, capacity - weights[n-1]) + prices[n-1];
        // 两个值中取最大的
        return v1 > v2 ? v1 : v2;
    }
}


void initGoods();
void printGoods();

int main() {
    int result;

    initGoods();
    printGoods();

    result = maxPriceInBag(N, CAPACITY);
    printf("\nresult = %d\n", result);

    return 0;
}


// 产生一个随机值,该值位于 [down, up) 内.
unsigned randValue(unsigned down, unsigned up) {
    unsigned range = up - down;
    return rand() % range + down;
}

// 初始化物品
void initGoods() {
    int i;
    srand(time(0));

    for (i = 0; i < N; ++i) {
        weights[i] = randValue(1, 10);
        prices[i] = randValue(1, 100);
    }
}

// 打印物品
void printGoods() {
    int i;
    puts("\n");

    for (i = 0; i < N; ++i) {
        printf("%d: weight = %d \t price = %d\n", i, weights[i], prices[i]);
    }
}

穷举法求解

设共3个物品,1 表示该物品放放背包内,0 表示没有放入,则

  • 001 表示了一种选择,即第3个物品放入,其它没有。

  • 则下一个选择分别是 010,011,100,101,110,111

  • 当出现 111(即全1)时,穷举结束。

参考实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define N 3
#define CAPACITY 10

int weights[N]; // 重量
int prices[N];  // 价值

int selected[N]; // 是否选中,1:选中, 0:没有。

// 枚举下一个选择组合,如果所有的情况都已穷举完,返回0.
// 如 010 下一个就是 011,接下来是 100,101, 110, 111
// 实现时采用末端加 1,依此向前进位。
int next() {
    int i;

    for(i = 0; i < N; i++) {
        if (selected[i] == 0) {
            selected[i] = 1;
            return 1;
        } else {
            selected[i] = 0;
        }
    }

    return 0;
}

// 根据 selected 将数组 a 相加,没有选中的不计入内
int sum(int *a) {
    int i, s = 0;

    for(i = 0; i < N; i++) {
        if (selected[i]) {
            s += a[i];
        }
    }

    return s;
}

int maxPriceInBag() {
    int i, maxPrice = 0;

    while (next()) {
        if (sum(weights) <= CAPACITY) {
            int price = sum(prices);
            if (maxPrice < price) maxPrice = price;
        }
    }
    return maxPrice;
}


void initGoods();
void printGoods();


int main() {
    initGoods();
    printGoods();

    int result = maxPriceInBag();
    printf("\nresult = %d\n", result);

    return 0;
}


// 产生一个随机值,该值位于 [down, up) 内.
unsigned randValue(unsigned down, unsigned up) {
    unsigned range = up - down;
    return rand() % range + down;
}


void initGoods() {
    int i;
    srand(time(0));

    for (i = 0; i < N; ++i) {
        weights[i] = randValue(1, 10);
        prices[i] = randValue(1, 100);
    }
}

void printGoods() {
    int i;
    puts("\n");

    for (i = 0; i < N; ++i) {
        printf("%d: weight = %d \t price = %d\n", i, weights[i], prices[i]);
    }
}

思考与练习

  • 要使装入背包中的物品其总价值最大,要选择哪些物品? 试修改程序以打印出选择结果。

posted on 2012-02-11 15:14  wuqq  阅读(102)  评论(0)    收藏  举报