完全背包

完全背包问题:已知有n中物品和一个容量为v的背包,每种物品均有无限件,第i种物品的费用为c[i],价值是w[i]
求如何放使得物品费用总和不超过背包容量,且价值总和最大。

方法1:
仿照01背包,对每件物品的取件数再穷举,即确定每件物品的件数范围,然后当做01背包处理。
f[i][j] = max{f[i-1][v-k*c[i]] + k*w[i]} 0<=k*c[i]<=v

方法2:(优化)
动归方程的优化。
f[i][j] = max{f[i-1][v],f[i][v-c[i]]+w[i]}   第i中物品放与不放
公式分析:在加选第i件物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]]
时间复杂度为O(n*v)

初始化:若不是恰好装满f[i][0]和f[0][j]均初始化为0
若是恰好装满f[i][0]均初始化为0, f[0][j](j>=1)初始化为无穷inf
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 105
using namespace std;

const int inf = -0x3f3f3f3f;

int n, v;
int dp[N][N], c[N], w[N];

void init()
{
	for (int i = 0; i <= n; i++)
		dp[i][0] = 0;
	for (int i = 1; i <= n; i++)
		dp[0][i] = 0;
}

int main()
{
	scanf("%d%d", &n,&v);
	for (int i = 1; i <= n; i++)
		scanf("%d%d", &c[i], &w[i]);
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= v; j++)
		{
			if (j < c[i])//不能放
				dp[i][j] = dp[i - 1][j];
			else
				dp[i][j] = max(dp[i - 1][j], dp[i][j - c[i]] + w[i]);
		}
	}
	printf("%d\n", dp[n][v]);
	return 0;
}
滚动数组实现。
动归方程
for i: 1->n
 for j: c[i]->v
  do f[j] = max{f[j],f[j-c[i]]+w[i]}
与零一背包的区别主要是第二层循环是正序,原因在于完全背包的特点是每件物品可以选无限件,因此加选第i件
物品时,正需要一个可能已选入第i中物品的子结果f[i][v-c[i]],所以用正循环。
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 100005
using namespace std;

int n, v;//物品的种数,背包的容量
int dp[N], c[N], w[N];

void init()
{
	for (int i = 0; i <= n; i++)
		dp[i] = 0;
}

int main()
{
	while (scanf("%d%d", &n, &v))
	{
		init();
		for (int i = 1; i <= n; i++)
			scanf("%d%d", &c[i], &w[i]);
		for (int i = 1; i <= n; i++)
			for (int j = c[i]; j <= v; j++)
				dp[j] = max(dp[j], dp[j - c[i]] + w[i]);//表示放与不放
		printf("%d\n", dp[v]);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值