0/1背包问题(回溯法)

76 阅读2分钟

问题描述

有N件物品和一个容量为C的背包。第i件物品的价值是P[i],重量是W[i]。求解将哪些物品装入背包可使价值总和最大。所谓0-1背包,表示每一个物品不能拆解,要么装入,要么不装入。

1、W=某个值,求最大价值

public class Knap {
    final int MAXN = 100;

    int W = 6;
    int[] w = { 0, 5, 3, 2, 1, 3 };
    int[] v = { 0, 4, 4, 3, 2, 9 };
    int n = w.length - 1;
    int[] x = new int[MAXN];
    int maxv = 0;
    
    void disp() {
        for (int i = 1; i <= n; i++) {
            if (x[i] == 1)
                System.out.printf(i + "  ");
        }
        System.out.println();
        System.out.println(maxv);
    }

    void p() {
        int cv = 0;
        int cw = 0;

        int rw = 0;
        for (int i = 0; i <= n; i++)
            rw = rw + w[i];
        int rv = 0;
        for (int i = 0; i <= n; i++)
            rv = rv + v[i];
        int[] op = new int[w.length];
        
        dfs(1, cw, cv, rw, rv, op);
        disp();

    }
    
    public void dfs(int i, int cw, int cv, int rw, int rv, int[] op) {
        if (i > n) {
            if (cw == W && cv > maxv) {
                maxv = cv;
                System.arraycopy(op, 0, x, 0, n + 1);
            }
            return;
        }
        // 左剪枝
        if (cw + w[i] <= W && cv + rv > maxv) {
            op[i] = 1;
            dfs(i + 1, cw + w[i], cv + v[i], rw - w[i], rv - v[i], op);
        }
        // 右剪枝
        if (cw + rw -w[i]>=W && cv + rv - v[i] > maxv) {
            op[i] = 0;
            dfs(i + 1, cw, cv, rw - w[i], rv - v[i], op);
        }
    }

    public static void main(String[] args) {
        new Knap().p();
    }

}

2、W<=某个值

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Goods {
    int no;
    int w;
    int v;
    Goods(int no, int w, int v) {
        this->no = no;
        this->w = w;
        this->v = v;
    }
    bool operator<(const Goods &s) const {
        return (double)v / w > (double)s.v / s.w; // 按单位价值降序
    }
};

vector<Goods> g;
int W, n;
vector<int> x, bestx;
int bestv = 0;

// 计算上界函数
double bound(int cw, int cv, int i) {
    int rw = W - cw;
    double b = cv;
    int j = i;
    while (j < n && g[j].w <= rw) {
        rw -= g[j].w;
        b += g[j].v;
        j++;
    }
    if (j < n)
        b += (double)g[j].v / g[j].w * rw;
    return b;
}

// 深度优先搜索
void dfs(int cw, int cv, int i) {
    if (i == n) {
        if (cv > bestv) {
            bestv = cv;
            bestx = x;
        }
        return;
    }

    // 左孩子:选择物品 i
    if (cw + g[i].w <= W) {
        x[i] = 1;
        dfs(cw + g[i].w, cv + g[i].v, i + 1);
    }

    // 右孩子:不选择物品 i(剪枝判断)
    double b = bound(cw, cv, i + 1);
    if (b > bestv) {
        x[i] = 0;
        dfs(cw, cv, i + 1);
    }
}

// 背包问题求解函数
void knap(vector<Goods> g1, int W1) {
    g = g1;
    W = W1;
    n = g.size();
    sort(g.begin(), g.end()); // 按单位价值降序排列
    x = vector<int>(n, 0);
    bestx = vector<int>(n, 0);
    bestv = 0;

    dfs(0, 0, 0);

    // 输出结果
    cout << "最大价值: " << bestv << endl;
    cout << "最佳装填方案:" << endl;
    for (int i = 0; i < n; i++) {
        if (bestx[i] == 1) {
            cout << "选取物品编号: " << g[i].no << endl;
        }
    }
}

int main() {
    vector<Goods> items = {
        Goods(0, 5, 4),
        Goods(1, 3, 4),
        Goods(2, 2, 3),
        Goods(3, 1, 1)
    };
    int capacity = 6;

    knap(items, capacity);
    return 0;
}