背包

背包问题

01背包:每个物品:选、不选

完全背包:每个物品:选0,选1个,选2个,....选无穷多个


多重背包问题(每个物品:选0....选si个)

多重背包问题Ⅰ(朴素版):

点击查看代码
// 数据范围
// 0<N,V≤100
// 0<vi,wi,si≤100

#include<iostream>

using namespace std;

const int N = 110;
int v[N], w[N], s[N];
int m, n;
int f[N][N];

int main() {
    // 物品种数  背包容积
    cin >> m >> n;
    for (int i = 1; i <= m; i++) {
        cin >> v[i] >> w[i] >> s[i];
    }
    
    for (int i = 1; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            for (int k = 0; k <= s[i] && k * v[i] <= j; k++) {
                f[i][j] = max(f[i][j], f[i-1][j-k*v[i]] + w[i]*k);
            }
        }
    } 
    cout << f[m][n] << endl; 
    return 0;
}

多重背包问题Ⅱ(对多重背包问题Ⅰ进行二进制的优化):

点击查看代码
// 数据范围
// 0<N≤1000
// 0<V≤2000
// 0<vi,wi,si≤2000
#include<iostream>
#include<algorithm>

using namespace std;

// 时间复杂度为N * V * logS= 1000 * 2000 * log2000 
const int N = 25000;
int m, n;
int v[N], w[N], s[N];
int f[N];

int main() {
    // 物品种数  背包容积
    cin >> m >> n;

    // 当前物品的排序
    int cnt = 0; 
    for (int i = 1; i <= n; i++) {
        int a, b, c;
        // 表示体积 价值 数量
        cin >> a >> b >> c;
        
        // k表示当前选择当前物品的个数
        int k = 1;
        // 当选择物品个数大于给定个数时,退出循环
        while (k <= c) {
            cnt++;
            v[cnt] = a * k;
            w[cnt] = b * k;
            c -= k;
            k *= 2;
        }
        
        // 当还有剩余值时,继续加进来
        if (c > 0) {
            cnt++;
            v[cnt] = a * c;
            w[cnt] = b * c;
        }
    }
    
    // 更新物品的个数
    m = cnt;
    
    // 将多重背包问题变成了01背包
    // 对于每个组中的物品选或是不选
    for (int i = 1; i <= m; i++) {
        for (int j = n; j >= v[i]; j--) {
            f[j] = max(f[j], f[j-v[i]] + w[i]);
        }
    }

    cout << f[n];

    return 0;
}

多重背包问题Ⅲ(对多重背包问题Ⅰ进行单调队列优化/滑动窗口求数值):(不太懂)



点击查看代码
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 20010;

int n, m;
int f[N], g[N], q[N];

int main() {
    // 分别表示物品种数 背包体积
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        int v, w, s;
        cin >> v >> w >> s;
        
        // 将i-1层(上一层)的价值存储下来
        memcpy(g, f, sizeof f);
        
        for (int j = 0; j < v; j++) {
            int hh = 0, tt = -1;
            for (int k = j; k <= m; k += v) {
                // 当整个队列中的元素超出s个时,就让队头元素出去
                if (hh <= tt && q[hh] < k - s * v)  {
                    hh++;
                }
                
                // 下标减去初始的值
                // 将尾部元素的价值比当前的元素的价值小的时候,将队尾弹出
                // 当前下标和该最大值的下标之间差了x个v,那么就要加上x个w
                while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) {
                    tt--;
                }
                
                // 如果队列不空的话,求得的是窗口内部的最大值。中间空余了x个物品,将x乘以w
                if (hh <= tt) {
                    f[k] = max(f[k], g[q[hh]] + (k - q[hh]) / v * w);
                }
                
                q[++tt] = k;
            }
        }
    }
    
    
    cout << f[m] << endl;
    
    return 0;
}
posted @ 2022-05-06 18:00  jsqup  阅读(12)  评论(0编辑  收藏  举报