背包问题有感

博客内容讲述了动态规划在解决01背包、完全背包、多重背包以及分组背包问题中的应用,强调了逆序遍历的重要性以避免重复计算,并展示了不同类型的背包问题的代码实现,包括如何将二维状态压缩为一维,以及如何优化循环条件来提高效率。
摘要由CSDN通过智能技术生成

01二维:二维下的状态定义f[i][j]是前 i件物品,背包容量 j下的最大价值

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;

int v[N],w[N];
int f[N][N];
int n,m,k;
int main()
{
	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-1][j-w[i]]+v[i]); 
		}
	}
	cout<<f[n][m]<<endl;
	return 0;
}

二维换一维:f[j]就是前i轮已经决策的物品且背包容量 j 下的最大价值

逆序:为什么要逆序呢?

在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,f[i][j]与f[i - 1][j]是独立的。而优化到一维后,如果我们还是正序,则有可能是现在这一轮的f[较小体积]更新到现在这一轮的f[较大体积],但是我们需要的是从i-1轮得到的状态(因为要看没有加该物体时的情况下加不加谁更优)而我们现在这一轮的f[较小体积]有可能已经考虑过这个物体了,会重复考虑。比如说第i层考虑体积3的物体,f[7]由没有考虑该物体的i-1层的max(f[4],f[4]+v[该物体])更新得来。

(1)从小到大0-m:f[4]先计算,私下里更新成了f[i][4],当我们再次遍历f[7]时用的是第i层的f[4],这时候里面的现在的i物体可能已经被决策过了,加入背包里了,可能会重复加入。

(2)从大到小m-0:f[7]先计算,f[7]由f[4]更新而来,f[4]在i层没有被计算过,没有加入到现在的决策背包中。里面所有的价值决策都是从上一层i-1层得来的。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;

int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
	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=m;j>=0;j--)
		{
			if(j<w[i])//放不下的话就更新成上一层更新过的f[j] 
				f[j]=f[j];//不变,可以省略 
			else
				f[j]=max(f[j],f[j-w[i]]+v[i]); 
		}
	}
	cout<<f[m]<<endl;
	return 0;
}

我们发现只有当枚举的背包容量 >= v[i] 时才会更新状态,因此我们可以修改循环终止条件进一步优化。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;

int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
	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=m;j>=w[i];j--)
			f[j]=max(f[j],f[j-w[i]]+v[i]); 
	}
	cout<<f[m]<<endl;
	return 0;
}

完全背包:换一下二层循环顺序:因为可以从当前层考虑,反正可以无限次选用

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 1005
using namespace std;

int v[N],w[N];
int f[N];
int n,m,k;
int main()
{
	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=w[i];j<=m;j++)
			f[j]=max(f[j],f[j-w[i]]+v[i]); 
	}
	cout<<f[m]<<endl;
	return 0;
}

多重背包:

拆成01背包:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 100010
using namespace std;
int w[N],v[N],s[N];
int dp[N];
int n,m;

int main()
{
	int a,c,b,t=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
	{
		cin>>a>>b>>c;
		while(c--)
		{
			v[++t]=a;
			w[t]=b;			
		}
	} 
    for(int i=1;i<=t;i++)
    {
        for(int j=m;j>=v[i];j--)
        {
        	dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
    }
    cout<<dp[m]<<endl;
    return 0;
}

朴素版一维:

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 110
using namespace std;
int w[N],v[N],s[N];
int dp[N];
int n,m;

int main()
{
    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=m;j>=1;j--)
		{
			for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
			{
				dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
			}
		}
	 }
	 cout<<dp[m]<<endl; 
    return 0;
}

分组背包:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 110

using namespace std;

/*struct node{
	int a,b;//a组号,b个人编号 
}v[N],w[N];*/
int v[N][N],w[N][N];
int n,m,t;//n组数、m容量 
int s[N];//该组个数
int f[N][N];
 
int main()
{
	cin>>n>>m;
	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=0;j<=m;j++)
		{
			f[i][j]=f[i-1][j];//不选的话 
			for(int k=1;k<=s[i];k++)//从编号1开始看看选不选 
			{
				if(j>=v[i][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;
 } 

分组背包一维优化:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 110

using namespace std;

/*struct node{
	int a,b;//a组号,b个人编号 
}v[N],w[N];*/
int v[N][N],w[N][N];
int n,m,t;//n组数、m容量 
int s[N];//该组个数
int f[N];
 
int main()
{
	cin>>n>>m;
	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=m;j>=0;j--)
		{
			for(int k=1;k<=s[i];k++)
			{
			    if(j>=v[i][k])
	    			f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
			}
		}
	}
	cout<<f[m]<<endl;
	return 0;
 } 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值