0-1背包问题(动态规划 空间优化) 完全背包 背包贪心

0-1背包问题

给定n种物品和一背包。物品重量wi,价值vi,背包容量c。如何选择物品使得装入背包中的价值最大?
状态表示
m(i,j),1<=i<=n,0<=j<=c,表示背包可用容量j,待考虑装包的物品集{i,i+1,…n}时的最大装入物品价值。
转态转移方程:
边界:m(n,j)=0 第n个物品不放进去(因为容量不够),j<wi;
=Vn 第n个物品放进去,j>=wi;
一般:m(i,j)=m(i+1,j) 第n个物品不放进去(容量不够),j<wi;
=max{m(i+1,j),m(i+1,j-wi)+vi},容量够,j>=wi;
最终m[1][c]即原问题的解。

关键代码:

void knacksack(int m[][1000],int n,int c,int w[],int v[])
{
	for(int j=0;j<=c;++j)//边界,即当前只处理第n个物品 
		if(j>=w[n]) m[n][j]=v[n]; 
		else m[n][j]=0;
	for(int i=n-1;i>=1;--i)//自第n-1行向上填充表 
		for(int j=0;j<=c;++j) 
		if(j<w[i]) m[i][j]=m[i+1][j];//容量不足,第i个物品装不下
		else m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
		//容量够,进行选择 
} 

总代码

#include <iostream>
using namespace std; 
void knacksack(int m[][1000],int n,int c,int w[],int v[])
{
	for(int j=0;j<=c;++j)//边界,即当前只处理第n个物品 
		if(j>=w[n]) m[n][j]=v[n]; 
		else m[n][j]=0;
	for(int i=n-1;i>=1;--i)//自第n-1行向上填充表 
		for(int j=0;j<=c;++j) 
		if(j<w[i]) m[i][j]=m[i+1][j];//容量不足,第i个物品装不下
		else m[i][j]=max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
		//容量够,进行选择 
} 

void traceback(int m[][1000],int n,int c,int w[],int v[],int x[])//输出物品装入背包的情况
{
		for(int i=1;i<n;++i)
			if(m[i][c]==m[i+1][c])
			//因为我们求的其实是m[1][c]
			//所以traceback从m[1][c]开始 
				x[i]=0;//用数组x记录是否装入
			else{
				x[i]=1;//第i个物品装入了
				c=c-w[i]; 
			}
		if(m[n][c]>0) x[n]=1;//因为i=n时进入循环体数组会越界 
		else x[n]=0;
}
int main()
{
	int n;cin>>n;//物品数量
	int c;cin>>c;//容量
	int m[n+1][1000];//表示不同状态下的价值 
	int w[n+1],v[n+1];
	for(int i=1;i<=n;++i)//输入重量 价值 
		cin>>w[i];       //注意数组下标都从1开始 
	for(int j=1;j<=n;++j)
		cin>>v[j]; 
	int x[n+1];//记录背包放置情况 
	knacksack(m,n,c,w,v);	
	traceback(m,n,c,w,v,x);
	cout<<"最大价值:"m[1][c]<<endl;
	cout<<"背包放入情况:"; 
	for(int i=1;i<=n;++i)
		cout<<x[i]<<' ';
	return 0;
}


0-1背包问题空间优化

可以发现0-1背包的状态转移方程 dp[i][j] = max{dp[i+1][j-w[i]]+v[i],dp[i+1][j]}的特点,当前状态仅依赖前一状态的剩余体积与当前物品体积v[i]的关系(如果从表格的角度(i,j)位置的值只依赖于下一行中正下方和下一行中左前方位置的值)。根据这个特点,我们可以将dp降到一维即dp[j] = max{dp[j],dp[j-w[i]]+v[i]}。

但需要注意:用二维数组时,循环体内j(剩余容量)的值是递增式填充;但一维的时候要从大到小填充(如果从小到大:由于是一维数组,会改变前面数组的值,影响了数组后面的数据更新)
关键代码

void knacksack(int m[],int n,int c,int w[],int v[])//空间优化 
{
	for(int j=0;j<=c;++j)//当于初始化 第n行数据 
		if(j>=w[n]) m[j]=v[n]; 
		else m[j]=0; 
	for(int i=n-1;i>=1;--i)
		for(int j=c;j>=w[i];--j) //容量从大到小来
		//只有j>=w[i]更新 否则维持原值不变 
			 m[j]=max(m[j],m[j-w[i]]+v[i]);
		
} 

完全背包问题

问题描述:和0-1背包问题唯一区别在于每个物品数量是无限的
关键代码
该代码中没有显示边界处理的代码(和上方的0-1背包边界处理一样)

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

背包问题之贪心算法

此时放入背包可以只放入物品的一部分(就是不一定要完整放入)。我们的贪心策略是:求出每个物品单位重量的价值,将单位重量价值高的物品先放入背包.

#include <iostream>
#include <algorithm> 
using namespace std;
struct bag{
	float w;//物品的重量 
	int v;//物品的价值 
	float vw;//物品单位重量的价值 
	float x;//使用率:1代表物品完整放入,小于1代表被分割后放入	
};
bool cmp(bag &a,bag &b)
{
    return a.vw>b.vw;
}
int main()
{
	int n;cin>>n;//物品数量
	bag b[n];//定义n个结构体
	int c;cin>>c;//背包max容量 
	for(int i=0;i<n;++i)
		cin>>b[i].w;
	for(int i=0;i<n;++i)
		cin>>b[i].v;
	for(int i=0;i<n;++i)
	{	
		b[i].vw=b[i].v/b[i].w;//单位质量价值
		b[i].x=0;//使用率初始化0 
	}
	
	sort(b,b+n,cmp);//按单位质量价值排序
	int k;
	for(k=0;k<n;++k)
	{
		if(b[k].w>c) break;
		b[k].x=1;
		c-=b[k].w; 
	}
	if(k<=n-1) b[k].x=c/b[k].w;//最后一个物品可能只装了一部分 
	
	for(int i=0;i<n;++i)
	{
		cout<<"重:"<<b[i].w<<"、价值:"<<b[i].v<<"的物品被放入了背包"<<endl<<"放入比例:"<<b[i].x<<endl;
	}
} 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值