背包问题

一:01背包

(1):01背包分析

简单描述:就是有n个物品,包的体积为v,每个物品分别体积 和 价值为vi,wi,问得到的最大价值是多少。

首先我们对每一个物品都有两种选择,选或不选所以设f[i,j]的意思为在前i个物品中选择体积不超过j。
因为仅有两种选择,所以f[i,j]=max(f[ i - 1, j ],f[ i - 1, j - v ] + w );其中一个是在前面i-1个选择完了,不选择第i个,另一个是选择第i个.

(2):二维背包代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1010;
int w[maxn],v[maxn];
int f[maxn][maxn];
int main()
{
    int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=V;j++)
        {
            if(j<v[i]) f[i][j]=f[i-1][j];
            else 
            f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
        }
    }
    printf("%d",f[N][V]);
    return 0;
}

(3):空间优化代码

#include<iostream>
using namespace std;
#define N 1005
int dp[N]; 
int main()
{
	int n,m,v,w;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
	    cin>>v>>w;
		for(int j=m;j>=v;j--)
		{
			dp[j]=max(dp[j],dp[j-v]+w);
		} 
	}
	cout<<dp[m]<<endl;
	return 0;
}

这里我们可以观察到在二维体积遍历时时倒着往前遍历,因为观察原始方程,我们可以发现后面的两个式子都是需要的i-1的即上一维的数据,如果我们从小到大遍历,那么dp[5]的数据应该是dp[5] 需要的数据可能要上一维的dp[4],而在遍历dp[5]的时候第i维的dp[4]已经计算出来了,所以更新是由第i维实现的,而不是由i-1维。而倒着就因为前面的还没有更新,所以能够与原来的等价。

二:完全背包

(1):完全背包分析

有n种物体,每个物体都可以选择无限次,每个物体的价值和体积分别为 wi vi,f[i,j]分别表示在前i个物品中选择体积不超过j;

(2):方程推导

f[ i , j ] = max( f[ i - 1 , j ] , f[ i - 1 , j - v ] + w , f[ i - 1 , j - 2v ] + 2w , f[ i - 1 , j - 3v ] + 3w …)
f[ i , j - v ] = max(f[ i - 1 , j - v ] , f[ i - 1, j - 2v] + w, f[ i - 1 , j - 3v ] + 2w…)
我们可以发现以上规律可以写为 f[ i , j ] = max( f[ i - 1 , j ] , f[ i , j - v ] + w ) ;

(3):完全背包代码

#include<bits/stdc++.h>
using namespace std; 

const int N = 1005;
int f[N][N], w[N], v[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; ++i) 
        cin >> w[i] >> v[i];
    for(int i = 1; i <= n; ++i) 
        for(int j = 1; j <= m; ++j)
        {
            if(j < w[i]) f[i][j] = f[i - 1][j];
            else f[i][j] = max(f[i - 1][j], f[i][j - w[i]] + v[i]);
        }           
    cout << f[n][m] << endl;
    return 0;
}

(4):空间优化的代码

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
int v[maxn],w[maxn];
int f[maxn];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    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]);
        }
    }
    printf("%d\n",f[m]);
    return 0;
}

优化说明:我们发现在这里完全背包和01背包有所不同,这里的第二项是i而不是i-1,所以要从0开始,第一个而第一个i-1不必考虑,因为每次计算的时候都是从上次计算的,而第二个在计算f[ i , j ] 时,f[ i , j - v ]在本层已经计算过所以从0开始遍历。

三:多重背包

(1):多重背包分析

每个物品最多可以选择s个,其他的和其余背包相同。

(2):多重背包代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int f[110];
int main()
{
    int n,v,m,w,s;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        cin>>v>>w>>s;
        for(int j=1;j<=s;j++)
        {
            for(int k=m;k>=v;k--)
            f[k]=max(f[k],f[k-v]+w);
        }
    }
    printf("%d\n",f[m]);
    return 0;
}

分析:对于这个多重背包的理解,我认为就是在枚举个数后和01背包是一样的,我们可以发现这体积的循环在最内层,我们可以把外面两层循环看做是对n * s 种情况的枚举,可以看做01背包。

(3):多重代码时间优化

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int dp[2020];
struct good
{
    int w,v;
};
int main()
{
    vector<good> goods;
    int N,V,w,v,s;
    cin>>N>>V;
    for(int i=1;i<=N;i++)
    {
        cin>>v>>w>>s;
        for(int k=1;k<=s;k*=2)
        {
            s-=k;
            goods.push_back({k*w,k*v});//将多重背包中的含有多种同一物品的情况例如 7 化为 1 2 4 情况这样就可以就可以将一个问题化为选与不选的问题。
        }
        if(s>0) goods.push_back({s*w,s*v});
    }
    for(auto t:goods)
    {
        for(int j=V;j>=t.v;j--)
        {
            dp[j]=max(dp[j],dp[j-t.v]+t.w);
        }
    }
    printf("%d\n",dp[V]);
    return 0;
}

分析:这个是二进制优化,优化的原理是在0~n的范围内,通过1不断乘以2记录下每个数,知道大于n, 这些数可以组成每个0 ~ n的数,例如:7:1,2,4。然后把s这一维给优化了,通过分成1,2,4这些数,原本的枚举现在可以变成选还是不选的。我认为他优化点就是去了一层循环。

四:混和背包

(1):混合背包分析

三种背包的混合

(2):混合背包代码

题目链接


//主要思路就是将多重背包转化为01背包,然后01 和完全分情况讨论
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
int dp[2020];
struct good
{
   int w,v,s;   
};
int main()
{
    vector<good> goods;
    int N,V,v,w,s;
    cin>>N>>V;
    for(int i=1;i<=N;i++)
    {
        cin>>v>>w>>s;
        if(s==-1) goods.push_back({w,v,-1});
        if(s==0) goods.push_back({w,v,0});
        if(s>0)
        {
            for(int j=1;j<=s;j*=2)
            {
                s-=j;
                goods.push_back({j*w,j*v,-1});
            }
            if(s>0) goods.push_back({s*w,s*v,-1});
        }
    }
    for(auto t:goods)
    {
        if(t.s==-1)
        {
            for(int j=V;j>=t.v;j--)
            dp[j]=max(dp[j],dp[j-t.v]+t.w);
        }
        else if(t.s==0)
        {
            for(int j=0;j<=V;j++)
            if(j>=t.v)
            dp[j]=max(dp[j],dp[j-t.v]+t.w);
        }
    }
    printf("%d\n",dp[V]);
    return 0;
}

这个的解题思路就是将多重背包转化为01背包,然后再分别处理01和完全

五:二维费用背包

(1):二维费用背包分析

多加了一个限制条件,例如,受限制的不仅是背包体积还有背包的承重量

(2):二维费用背包代码

题目链接

#include<iostream>
#include<algorithm>
using namespace std;
int dp[2020][2020];
int main()
{
    int N,V,M,m,v,w;
    cin>>N>>V>>M;
    for(int i=1;i<=N;i++)
    {
        cin>>v>>m>>w;
        for(int j=V;j>=v;j--)
        for(int k=M;k>=m;k--)
        {     
            dp[j][k]=max(dp[j][k],dp[j-v][k-m]+w);
        }
    }
    printf("%d\n",dp[V][M]);
    return 0;
}

这个的解题思路就是无非是多加了一个最大承重量,因为这里k和v是息息相关的,代码中也有体现,两者是同是加减的,减去一个物品体积的同时,也要减去他的重量,所以体积是倒序的,质量自然也是倒序的。

六:分组背包

(1):分组背包分析

就是一类物品中不一定有一个物品,但只能选择一个。

(2):分组背包代码

题目链接

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

int v[110][110],w[110][110],dp[110],s[110];
int main()
{
   int N,V;
   cin>>N>>V;
   for(int i=1;i<=N;i++)
   {
       cin>>s[i];
       for(int j=1;j<=s[i];j++)
       {
           cin>>v[i][j]>>w[i][j];
       }
   }
   for(int i=1;i<=N;i++)
   {
       for(int j=V;j>=0;j--)
       {
           for(int k=1;k<=s[i];k++)//转化成01背包问题
           {
               if(j>=v[i][k])
               dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
           }
       }
   }
   printf("%d\n",dp[V]);
   return 0;
}

这个的解题思路就是也转化成01背包,但是我们可以发现,这个和多重背包的区别是这个对体积的遍历在二维,也就是和分组背包的区别之处,这里在二维的目的是在三维里遍历物品,保证每类物品仅仅可以选择一个,看选哪个物品最好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值