zl程序教程

您现在的位置是:首页 >  其他

当前栏目

动态规划之混合背包

2023-04-18 15:21:02 时间

题目描述


一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn。有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

输入

第一行:二个整数,V(背包容量,V<=200),N(物品数量,N<=30);
第2到N+1行:每行三个整数Wi,Ci,Pi,前两个整数分别表示每个物品的重量,价值,第三个整数若为0,则说明此物品可以购买无数件,若为其他数字,则为此物品可购买的最多件数(Pi)。

输出

仅一行,一个数,表示最大总价值。

样例输入:

10 6 3
2  1  1
3  3  1
4  8  2
6  9  2
2  8  3
3  9  3

 

样例输出:

20

 

 【样例解释】
选第一件物品1件和第三件物品2件。

分组背包思路:

分组背包的问题描述:
有N件物品和一个容量为V的背包,第 i 件物品的费用是w[i],价值是c[i]。这些物品被划分为若干组,每组中的物品互相冲突,而且每个组中只能选择一件。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包的容量并且价值总和为最大。

  • emmm,这个问题其实就是将物品分了一个组。对于之前的背包问题,我们是将一件件的物种挨个考虑进来,对于这一类的问题,我们是将每一组挨个考虑进来。(就是第一层本来表示物品种类的循环换成表示各个组的循环)
  • 然后在考虑每个单元格的时候,在单元格中选择最优的时候考虑的是这个组中的物品,将组中的物品考虑一遍,选择最优方案。(注意 要看一下这个组中的物品的重量是否是现在这个单元格 j 能够承受的)
  • 三层for循环

伪代码:(其实这个描述可以看做是分组的 01 背包)
for 所有的组i
–for j=m…0 //01 背包 从后向前考虑 无后效性
----for 所有的属于第 i 组的物品k //每个组中只能最多加一件物品
------f[j] = max(f[j],f[v-w[k]]+c[k])

本题分析:

1、本题中对于各个组的记录可以使用结构体记录
2、本题实际上是一种分组的01背包
3、本题是求最大结果,所以初始化都为0
4、题目中代价是用来限制所放物品不能够超过的,所以应由之前的单元格确定当前单元格。
5、所求范围:一维数组中考虑完所有的组的能装的最大值就可,也就是最后一个单元格。

 

原来的背包问题是第i件物品选与不选,而到了分组背包,只需要改成在一个组里面选第几个或是不选这和组。我们令F[i][j]表示前i组选j重量的物品,所以可以推导出动态转移方程:
F[i][j]=max(F[i-1][j],F[i-1][j-V[i][k]]+W[i][k]) (k表示选择第i组里的第k件)

6层循环解法(真的能AC):

#include<bits/stdc++.h>
using namespace std;
const int N=50;
const int V=250;
int W,n,g;
vector<int>good[N];
int f[V];
int w[N],v[N];
int main(){
    cin>>W>>n>>g;
    for(int i=1;i<=n;i++){
        int p ;
        cin>>w[i]>>v[i]>>p;
        good[p].push_back(i);
    }
    for(int i=1;i<=n;i++){
        for(int k=W;k>0;k--){
            for(int j=0;j<good[i].size();j++){
                int p=good[i][j];
                if(k>=w[p])
                    f[k]=max(f[k],f[k-w[p]]+v[p]);
            }
        }
    }
    cout<<f[W]<<endl;
    return 0;
}

 

动态规划解法:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
//分组背包 以各个组来考虑,每个单元格中遍历这个组的所有物品,因为一个组中只能拿一个物品
struct fenzu { //记录各个组
    int w[31];
    int c[31];
    int len=0;
};

int main() {
    int v,n,t;
    cin>>v>>n>>t;//背包容量、物品数量、最大组号
    fenzu zu[11];
    int a[201];//压缩的一位数组
    memset(a,0,sizeof(a));//求在代价内的最大值,所以全初始化为0
    int wl,cl,tl,len_0;//临时记录
    for(int i=1; i<=n; i++) {
        cin>>wl>>cl>>tl;
        len_0=++zu[tl].len;//先自加 后赋值
        //cout<<len_0<<endl;
        zu[tl].w[len_0]=wl;
        zu[tl].c[len_0]=cl;
    }

//分组 01背包  三重循环
    for(int i=1; i<=t; i++) { //分别逐步考虑各个组
        for(int j=v; j>=0; j--) { //遍历整个代价范围,压缩的数组
            for(int k=1; k<=zu[i].len; k++) { //在这个组中这么多的物品中,寻找在放在这个单元格中的最好方案
                if(j>=zu[i].w[k]) //如果能够装下的话,就如下,如果不能装下的话就考虑下个物品
                    a[j] = max(a[j],a[j-zu[i].w[k]]+zu[i].c[k]);
            }
        }

        //检测
        /*for(int j=0;j<=v;j++)
         cout<<a[j]<<;
        cout<<endl; */
    }

    cout<<a[v]; //范围:一维数组中考虑完所有的组的能装的最大值就可

    return 0;
}