返回顶部

背包DP

背包DP

背包DP,正如其名,一般有它的体积(容量m),物品的价值、个数、体积。但大多数的背包问题都比较抽象或者很复杂,因而找出题中的物品及其属性、背包的容积往往是破题的关键。

1. 01背包

     该类型状态比较简单,一种物品只有拿或不拿两种状态,令f[i][j]表示在前i个物品中拿,剩余容量为j所能得到的最大价值,状态转移方程如下:
     f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
     利用滚动数组可把空间复杂度降低一个维度
     就变成了f[j]=max(f[j],f[j-v[i]]+w[i])
<两层for循环:>
for(int i=1;i<=n;i++){
	for(int j=m;j>=v[i];j--){
		f[j]=max(f[j],f[j-v[i]]+w[i]);
	}
}

2.多重背包

该类型题目有两种解题思路,一种是常规方法,即加一层循环物品个数。一种是进行二进制优化,进而转化为01背包,该方法效率也更高,因而应用比较广泛。

<法1:>
for(int i=1;i<=n;i++){
		for(int j=m;j>=v[i];j--){
            for(int k=1;k<=c[i];k++)
			    f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);	
		 }
     }
法2:>
for(int i=1;i<=n;i++){
    for(int j=1;j<=c[i];j<<=1){
        W[++t]=j*w[i];
		V[t]=j*v[i];
    }
	if(c[i]>0){
 		W[++t]=j*w[i];
		V[t]=j*v[i];
    }
for(int i=1;i<=t;i++)
    for(int j=m;j>=v[i];j--)
        f[j]=max(f[j],f[j-v[i]]+w[i];

3.完全背包

所拿物品没有个数限制,代码与01背包相似:

点击查看代码
for(int i=1;i<=n;i++)
    for(int j=v[i];j<=m;j++)
		f[j]=max(f[j],f[j-v[i]]+w[i];

4.分组背包

每组之中最多只能选一件物品。

     //g[x][0]记录x组中物品个数,其余位置记录物品序号。
     for(int i=1;i<=n;i++){
         scanf("%d%d%d",&v[i],&w[i],&x);
	     g[x][++g[x][0]]=i;
     }
     for(int i=1;i<=t;i++){
         for(int j=0;j<=m;j++){
             f[i][j]=f[i-1][j];
             for(int k=1;k<=g[i][0];k++){
	         if(j>=v[g[i][k]]){
	            x=g[i][k];
	            f[i][j]=max(f[i][j],f[i-1][j-v[x]]+w[x]);
                 }
              }
          }
      }

5.混合背包

该题型其实就是把上面的背包类型(除了分组背包)揉在了一起,但其实还是按照多重背包来处理就行,对于没有数量限制的物品,就把它的数量上限定为包的容积/物品的体积,再二进制优化为01背包就可以很快解决了。
代码与多重背包高度相似,在这里我就不再展示出来了(doge)。

6.二维费用背包

该题型名字非常唬人,但实际做起来也就那么回事,就是体积不唯一,可能有许多因素,只要在循环体积的位置分开循环其余因素即可。

主要代码:
for(int i=1;i<=n;i++){
    for(int j=mm;j>=m[i];j--)
	for(int k=mv;k>=v[i];k--)
	    f[k][j]=max(f[k][j],f[k-v[i]][j-m[i]]+w[i]);

例:NASA的食物计划

题目描述:

航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.

输入格式:

第一行两个数体积最大值(<400)和质量最大值(<400)
第二行 一个数 食品总数N(<50).
第三行-第3+N行
每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)

输出格式:

一个数所能达到的最大卡路里(int范围内)

​本题食物的体积和质量都成为了背包的体积,食物的卡路里成为了价值,再进行01背包即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int mm,mv,t,n,i,j,k;
int f[N][N],w[N],v[N],m[N];
int main(){
    cin>>mv>>mm>>n;
    for(i=1;i<=n;i++)scanf("%d%d%d",&v[i],&m[i],&w[i]);
    for(i=1;i<=n;i++){
        for(j=mm;j>=m[i];j--)
            for(k=mv;k>=v[i];k--){
	        f[k][j]=max(f[k][j],f[k-v[i]][j-m[i]]+w[i]);
            }
    }
    cout<<f[mv][mm];
    return 0;
}
// 注意不要把质量和体积一起循环,一定要分开循环!!!!
posted @ 2024-02-17 16:18  无敌の暗黑魔王  阅读(26)  评论(1)    收藏  举报