【代码模板】完全背包

目录

1. 题目描述

2. 算法构思:

1. 定义:

2. 算法:dp

3. 优化step1:减少循环层数

4. 优化step2:采用一维数组

3. 完整代码:


1. 题目描述

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。

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

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

2. 算法构思:

1. 定义:

v[i] :第i个物品的体积

w[i]:第i个物品的价值

f[i][j] :前i个物品放入体积为j的物品后,最大价值

2. 算法:dp

对于第i个物品,我们有一下几种选择

此过程与01背包类似,只需要再枚举第i个的个数即可,大家可以去看一下,再看这个会比较容易理解一点

(这里附上我写的01:【代码模板】01背包-CSDN博客    打广告 

言归正传,此时,一个伟大的朴素版三重循环二维数组代码就诞生了!!!

#include<bits/stdc++.h>
using namespace std;
int v[1005],w[1005];
int f[1005][1005];
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++)
		{
			for(int a=1;a<=j/v[i];a++)//循环第i个物品的个数
			{
				f[i][j]=max(f[i-1][j],f[i-1][j-a*v[i]]+a*w[i]);	
			}
		}
	}
	cout<<f[N][V];
	return 0;
} 

3. 优化step1:减少循环层数

核心代码:

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

证明过程:

我们发现

下面的与上面的一部分相对应(连起来的部分),只不过上面在下面对应部分的基础上多加了一个w[i],剩下一块没有对应的部分为f[i-1][j],所以我们只用比较这两部分的大小即可。

即 f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]) 这里f[i][j]在之前已经赋值成f[i-1][j]了

4. 优化step2:采用一维数组

思路跟01背包的差不多,我们在优化1的基础上改

核心代码:

for(int i=1;i<=N;i++)
{
	for(int j=v[i];j<=V;j++)
	{
		f[j]=max(f[j],f[j-v[i]]+w[i]);	
	}
}

同样的 j 的正搜反搜的问题:

发现 f[] 用的是这一轮的数,不受前面的影响,且如果j不从头开始,前面的数为0,不对,所以正着来

3. 完整代码:

朴素版(三重循环二维数组)

#include<bits/stdc++.h>
using namespace std;
int v[1005],w[1005];
int f[1005][1005];
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++)
		{
			for(int a=1;a<=j/v[i];a++)
			{
				f[i][j]=max(f[i-1][j],f[i-1][j-a*v[i]]+a*w[i]);	
			}
		}
	}
	cout<<f[N][V];
	return 0;
} 

优化版(二重循环二维数组)

#include<bits/stdc++.h>
using namespace std;
int v[1005],w[1005];
int f[1005][1005];
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++)
		{
			f[i][j]=f[i-1][j];
			if(j-v[i]>=0)
			{
				f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);	
			}
		}
	}
	cout<<f[N][V];
	return 0;
} 

优化plus++版(一重循环一维数组)

#include<bits/stdc++.h>
using namespace std;
long long int v[1000005],w[1000005];
long long int f[1000005];
int main()
{
	int N,V;
	cin>>V>>N;
	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<=V;j++)
		{
			f[j]=max(f[j],f[j-v[i]]+w[i]);	
		}
	}
	cout<<f[V];
	return 0;
} 

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值