背包问题!

01背包问题——每件物品最多只用一次

状态表示:f[i][j] 表示从前i个物品中选,重量不超过j的最大价值。
状态计算:f[i][j] =max(f[i-1][j] , f[i-1][j-v[i]] +w[i] )(当前状态可分为选了第i件物品的价值最大值和没选第i件物品的最大值)

详细描述:

有N件物品和一个容量是V的背包,每件物品只能使用一次

第i件物品的体积是vi  价值是wi。

求解将哪些物品装进背包,可使这些物品的总体积不超过背包容量,且总价值最大。

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

分析:

f[i][j]:

数组含义:[0,i]之间物品任取,放进容量为j的背包

1.不含第i个物品:f[i][j] = f[i - 1][j];

不选(跳过)这个物品,那么容量j就不会改变:即从[0,i-1]之间的物品随便取,因为不放物品i,那么背包容量的最大价值是不是就和i-1的最大价值是一样的

2.选第i个物品:f[i][j] = f[i - 1][j - v[i]]+w[i];

直接求不好求,先预留第i个物品的空间,最后再加上第i个物品价值

物品量\背包容积012345
0000000
1(价值2)022222
2(价值4)024666
3(价值4)024668
4(价值5)024668
#include<stdio.h>
#include<iostream>
 #include<algorithm>
 using namespace std;
 const int N = 1010;//定义最大物品的数量
 int n,m;//n表示物品数量,m表示背包容积
 int v[N],w[N];//vi表示体积,wi表示价值
 int f[N][N];//fij表示所有方案中,从前i个物品中选并体积不超过j的最大价值
 int main()
 {
    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=1;j<=m;j++)
      {
        f[i][j]=f[i-1][j];
        //注意,包含第i个物品这种情况可能不存在,因此要判断 
        if(j>=v[i])//比较背包与物品体积
        {
            f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
           
        }
      }
    }
    cout<<f[n][m]<<endl;
    return 0;
 }

优化版

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;

int n,m;
int f[N]; //这里进行降维优化
int v[N],w[N];

int main()
{
    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 = m;j >= v[i];j--)
        {
            //由于此时变成了一位数组 由于比较的是和它前一位f[i-1][j-v[i]]为了更好地表示发生变化,我们应选择体积从最大的体积m依次递减
            f[j] = max(f[j],f[j - v[i]] + w[i]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

完全背包——每件物品能拿无数个

状态表示:f[i][j] 表示从前i个物品中选,重量不超过j的最大价值。
状态计算:f[i][j] =max(f[i-1][j] , f[i][j-v[i]] +w[i],f[i][j-2v[i]] +2w[i],……,f[i][j-kv[i]] +kw[i] )(当前状态可分为选了1-k件第i件物品的价值最大值和没选第i件物品的最大值

 

#include<stdio.h>
#include<iostream>
 #include<algorithm>
 using namespace std;
 const int N = 1010;//定义最大物品的数量
 int n,m;//n表示物品数量,m表示背包容积
 int v[N],w[N];//vi表示体积,wi表示价值
 int f[N][N];//fij表示所有方案中,从前i个物品中选并体积不超过j的最大价值
 int main()
 {
    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=1;j<=m;j++)
     	for(int k=0;k*v[i]<=j;k++)
            f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
               
    cout<<f[n][m]<<endl;
    return 0;
 }

优化版:


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

多重背包|——第i件物品最多可选si件

多个同种物品合成一件物品

把每件物品可选的情况拆分出来,把选择多件第i个物品转换成选择一个新的物品(如选k件则新物品表示为价值为kw[i],体积为kv[i])将问题转变为01背包问题。

#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int f[N][N],v[N],w[N],s[N];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	 cin>>v[i]>>w[i]>>s[i];
	for(int i=1;i<=n;i++)
	 for(int j=0;j<=m;j++)
	  for(int k=0;k*v[i]<=j&&k<=s[i];k++)
       f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+k*w[i]);
	cout<<f[n][m];	  
	return 0;
}

多重背包问题||——同多重背包问题优化

【【背包问题】多重背包问题 II】https://www.bilibili.com/video/BV1Q7411j7Vi?vd_source=cecc6ff6e883f42b10c1d53026d0846e 

二进制优化——>转化成01背包,2^n 件

 

#include<bits/stdc++.h>
using namespace std;
const int N=15000;
int f[N],v[N],w[N];
int main(){
	int n,m,tot=1;//变量存放打包的编号 
	cin>>n>>m;
	int a,b,s;//体积,价值,个数 
	for(int i=1;i<=n;i++){
	 cin>>a>>b>>s;
	 //开始打包 
	  for(int k=1;k<=s;k<<=1){//k*2 
	  	v[tot]=k*a;
	    w[tot++]=k*b;
	    s=s-k;
	    }
	    if(s>0)//打包完还有剩余的 比如10=1+2+4+3的3 
	    {
	    	v[tot]=a*s;
	    	w[tot++]=b*s;
		}
	}
	//回到01背包问题 
	for(int i=1;i<=tot;i++)
		for(int j=m;j>=v[i];j--)
		  f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m];

  
	return 0;
}

分组背包问题——分为n组,每组有ni个物品,每组最多只能选一件物品。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值