DP之背包问题

背包可以分为六大类:最基本的0/1背包、完全背包、多重背包、分组背包、混合背包和二维费用的背包。不管是什么类型的背包问题,最后都可以转化为0/1背包来做

0/1背包

板子题:有n件物品和一个容积为v的背包,第i件物品的体积为c[i],价值为w[i],求在不超过容积的情况下能装入物品的最大价值

当用二维的f数组进行状态转移时,f[i][j]表示i件物品放入容积为j的空间内取得的最大价值(注意前这个字 背包其实是资源分配中的一个分支),那么就可以得到状态转移方程:

f[i][j]=max(f[i-1][j],f[i-1][j-c[i]]+w[i])

f[i-1][j]表示不取这件物品,f[i-1][j-c[i]]+w[i]表示取

代码如下:


for (int i=1;i<=n;i++)
    for (int j=0;j<=v;j++)
    {
        f[i][j]=f[i-1][j];
        if (j>=c[i])
            f[i][j]=max(f[i][j],f[i-1][j-c[i]]+w[i]);
    }

或者可以用一维的f数组来进行状态转移,可以将空间复杂度优化为O(v),时间复杂度仍是O(n*v)

代码如下:

for (int i=1;i<=n;i++)
    for (int j=v;j>=0;j--)//变为反向搜,因为个数限制,在完全背包中就是0 to v do
    {
        if (j>=c[i])
            f[j]=max(f[j],f[j-c[i]]+w[i]);
    }

完全背包

板子题:即0/1背包的板子题,不同的是0/1背包每件物品只有一件,完全背包每种物品都有无数件可以取

算法1:将其转化为0/1背包做

二维的状态转移方程为:f[i][j]=max(f[i-1][j-k*c[i]]+k*w[i])

k*c[i]∈[0,j]

这种算法的时间复杂度比较高,所以有了第二种算法:

将0/1背包一维算法中的j层循环变为从0到v

在0/1背包中为了保证每件物品采用数量,所以要倒着搜,而完全背包中没有对数量的限制,所以j为0到v

多重背包

板子题:逃亡的准备

多重背包的每种物品有固定的数量a[i]件可以取,这个时候就要用一手二进制法(名字高端是不是)

对于一个数,比如10,可以将它分为1+2+4+3

这四个数可以组成1-10之间的每个数字(不信你可以试试),那么如果一种物品有10件可以取,就将其分为1,2,3,4的四种0/1背包的物品,最后我们还是把这一类型的背包转化为了0/1背包

代码如下:

cin >>n >>C;
for (int i=1;i<=n;i++)
	cin >>a[i] >>w[i] >>v[i];//分别是数量、重量和价值
for (int i=1;i<=n;i++)
{
	if (w[i]*a[i]>C)//在物品足够多的时候,对其做完全背包
	{
		for (int j=0;j<=C;j++)
		{
			if (j>=w[i])
			    c[j]=max(c[j],c[j-w[i]]+v[i]);
		}
	}
	else
	{
		int k=1,num=a[i];
		while(k<num)//是否取一个重量为k*w[i],价值为k*v[i]的物品
		{
			for (int j=C;j>=k*w[i];j--)
			    c[j]=max(c[j],c[j-k*w[i]]+k*v[i]);
		    num-=k;
		    k+=k;
		}
		for (int j=C;j>=num*w[i];j--)//剩下的作为单独的一个物品计算
			c[j]=max(c[j],c[j-num*w[i]]+num*v[i]);
	}
}

混合背包

将其每一种物品用对应的算法做。。。

分组背包

板子题:在0/1背包的基础上,将每个物品分为了k组,每组中的物品会冲突,只能取一件

设f[k][c]为前k组物品装入c时取得的最大价值

所以可以得到状态转移方程:

f[k][c]=max(f[k-1][c],f[k-1][c-w[i]]+v[i])

贴代码:

for (int k=1;k<=K;k++)
{
	for (int j=C;j>=0;j--)
	{
		i=每组k内所有物品
		{
		    if (j>=v[i])
			    f[j]=max(f[j],f[j-c[i]+w[i]);
		}
	}
}

推荐题:金明的预算方案

其实这题用分组背包是一种方法,正规方法应该是用树形DP做(多捞哦)

将每个主件,主件+附件1,主件+附件2,主件+附件1+附件2分为一组,套上分组背包的模版

下面感谢MHT提供的代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=40000;
int  f[300][maxn];
int q[100][3];
int p[100][3];
int m,n;
int main()
{
    int x,y,z;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
	cin>>x>>y>>z;
	if(z==0)
	{
		q[i][0]=x;
		p[i][0]=y;
	}
	else
	{
	    if(!q[z][1])
	    {
	        q[z][1]=x;
		p[z][1]=y;
	    }
	    else
	    {
	        q[z][2]=x;
		p[z][2]=y;
	    }
	 }
    }
    memset(f,0,sizeof(f));
    for(int i=1;i<=m;i++)
	for(int j=1;j<=n;j++)
	{
	    f[i][j]=f[i-1][j];
	    if(j>=q[i][0])
	    {
	        f[i][j]=max(f[i][j],f[i-1][j-q[i][0]]+p[i][0]*q[i][0]);
	    }
	    if(j>=q[i][0]+q[i][1])
	    {
	        f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][1]]+p[i][0]*q[i][0]+p[i][1]*q[i][1]);
	    }
	    if(j>=q[i][0]+q[i][2])
	    {
	        f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][2]]+p[i][0]*q[i][0]+p[i][2]*q[i][2]);
	    }
	    if(j>=q[i][0]+q[i][1]+q[i][2])
	    {
	        f[i][j]=max(f[i][j],f[i-1][j-q[i][0]-q[i][1]-q[i][2]]+p[i][0]*q[i][0]+p[i][1]*q[i][1]+p[i][2]*q[i][2]);
	    }
	}
    cout<<f[m][n];
    return 0;}

有依赖的背包问题

留坑哈哈哈哈哈哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值