背包问题

初始化细节:
若要求恰好装满背包,初始化时除了f[0]为0其它f[1…V]均设为-∞
若没有要求必须把背包装满,初始化时将f[0…V]全部设为0
0 1 :

for (int i = 1; i <= n; i++)//        物品数量
		for (int v = m; v >= w[i]; v--)//让v为最大容量m,递减,但要>=w[i];
			if (f[v - w[i]] + c[i] > f[v])
				f[v] = f[v - w[i]] + c[i];

完全背包:

for (int i = 1; i <= n; i++)
		for (int v = w[i]; v <= m; v++)//完全背包与01背包 不同的地方就是第二步,让v的初值为w  终值为最大容量m!!!!
			if (f[v] < f[v - w[i]] + c[i])
				f[v] = f[v - w[i] + c[i]];

例一: 0 1背包问题。

第二个 for从最大开始 然后递减 但要大于 物体的体积为止
【题目描述】
一个旅行者有一个最多能装 M 公斤的背包,现在有 n 件物品,它们的重量分别是W1,W2,…,Wn,它们的价值分别为C1,C2,…,Cn,求旅行者能获得最大总价值。

【输入】
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);

第2…N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。

【输出】
仅一行,一个数,表示最大总价值。

【输入样例】
10 4
2 1
3 3
4 5
7 9

【输出样例】
12
f[v]=max{f[v],f[v-w[i]] +c[i]};

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int w[2001], c[31], f[2004];
int main()
{
	int m, n; cin >> m >> n;//背包容量m和物品数量n
	for (int i = 1; i <= n; i++)//输入w重量和c价值
		cin >> w[i] >> c[i];
	for (int i = 1; i <= n; i++)//        物品数量
		for (int v = m; v >= w[i]; v--)//让v为最大容量m,递减,但要>=w[i];
			if (f[v - w[i]] + c[i] > f[v])
				f[v] = f[v - w[i]] + c[i];
	cout << f[m] << endl;
}

例二: 完全背包问题(物体不限数目)

第二个for从 物体的体积开始 然后体积递增 知道等于背包的容量!
【题目描述】
设有n种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为M,今从n种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于M,而价值的和为最大。

**【输入】
第一行:两个整数,M(背包容量,M≤200)和N(物品数量,N≤30);

第2…N+1行:每行二个整数Wi,Ci,表示每个物品的重量和价值。

【输出】
仅一行,一个数,表示最大总价值。

【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
max=12
f[v]=max{f[v],f[v-w[i]] +c[i]}

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int w[2001], c[31], f[2004];
int main()
{
	int m, n; cin >> m >> n;
	for (int i = 1; i <= n; i++)
		cin >> w[i] >> c[i];
	for (int i = 1; i <= n; i++)
		for (int v = w[i]; v <= m; v++)//完全背包与01背包 不同的地方就是第二步,让v的初值为w  终值为最大容量m!!!!
			if (f[v] < f[v - w[i]] + c[i])
				f[v] = f[v - w[i] + c[i]];
	cout << f[m] << endl;
}

例三: 多重背包问题:

第二个for从最大开始 最小为o!
【题目描述】
为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

【输入】
第一行二个数n(n≤500),m(m≤6000),其中n代表希望购买的奖品的种数,m表示拨款金额。

接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和能购买的最大数量(买0件到s件均可),其中v≤100,w≤1000,s≤10。

【输出】
一行:一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

输入样例】
5 1000
80 20 4
40 50 9
30 50 7
40 30 6
20 20 1
【输出样例】
1040

f[j]=max(f[j],f[j-kv[i]]+kw[i])

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int v[6002];//价格!
int w[6002];//价值
int s[6002];//物品的最大数量
int f[6002];
int max(int a, int b)
{
	return a >= b ? a : b;
}
int main()
{
	int n, m;
	cin >> n >> m;//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>=0;j--)//j是钱数  
			for (int k = 0; k <= s[i]; k++)//k是 物品数量
			{
				if (j - k * v[i] < 0) break;//如果 超过钱数 就break;
				f[j] = max(f[j], f[j - k * v[i]] + w[i] * k);//否则就把它赋值给f[j]
			}
	cout << f[m] << endl;
}

例四:背包问题的方案总数问题

组合的方案总数
第二个for 从最大开始,但要大于物体的a【i】;
0 1 背包也这样
【题目描述】
给你一个n种面值的货币系统,求组成面值为m的货币有多少种方案。

【输入】
第一行为n和m。

【输出】
一行,方案数。

【输入样例】
3 10 //3种面值组成面值为10的方案
1 //面值1
2 //面值2
5 //面值5
【输出样例】
10 //有10种方案

算法分析: 设f[j]表示面值为j的最大方案数,如果f[j-ka[i]]!=0,则f[j]=f[j]+f[j-ka[i]],

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m, n;
int a[1001];
long long f[10001];//要用long long
int main()
{
	cin >> n >> m;//n是 几种金额,m是总金额
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	f[0] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = m; j >= a[i]; j--)
			for (int k = 1; k <= j / a[i]; k++)
				f[j] = f[j] + f[j - k * a[i]];
	cout << f[m];
}

例五:开餐馆

【题目描述】
信息学院的同学小明毕业之后打算创业开餐馆.现在共有n个地点可供选择。小明打算从中选择合适的位置开设一些餐馆。这 n个地点排列在同一条直线上。我们用一个整数序列m1,m2,…mn来表示他们的相对位置。由于地段关系,开餐馆的利润会有所不同。我们用pi 表示在mi处开餐馆的利润。为了避免自己的餐馆的内部竞争,餐馆之间的距离必须大于k。请你帮助小明选择一个总利润最大的方案。

【输入】

输入第一行是整数 T(1≤T≤1000),表明有T组测试数据。紧接着有T组连续的测试。每组测试数据有3行。

第1行:地点总数n(n<100), 距离限制k(k>0且k<1000);

第2行:n 个地点的位置m1,m2,…mn(1000000>mi>0 且为整数,升序排列);

第3行:n 个地点的餐馆利润p1,p2,…pn(1000>pi>0 且为整数)。

【输出】
对于每组测试数据可能的最大利润。

【输入样例】
2
3 11
1 2 15
10 2 30
3 16
1 2 15
10 2 30
【输出样例】
40
30
思路:这个题 就不用再套用模板了,但要有那个思想,设出一个f数组来储存那些距离差符合题意 而且这两个地方的利益加在一起更大 把它赋给f 之后 因为f是 从i=1,然后与j=1,2,3,…n
,这样比较救能将多个地方的利益加在一起了

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m, n;
 long long w[111111], c[111111];
long long f[1000001];
int max(int a,int b)
{
	return a > b ? a : b;
}
int main()
{
	int t; cin >> t;
	while (t--)
	{
		memset(w, 0, sizeof(w)); memset(c, 0, sizeof(c));
		memset(f, 0, sizeof(f));
		cin >> n >> m;//n是地点总数,m是限制距离
		for (int i = 1; i <= n; i++)
			cin >> w[i];
		for (int i = 1; i <= n; i++)
		{
			cin >> c[i]; f[i] = c[i];
		}
		for(int i=1;i<=n;i++)
			for (int j = 1; j <= n; j++)
			{
				if (w[i] - w[j] > m)//如果两地距离符合标准
					f[i] = max(f[i], f[j] + c[i]);
			}
		int maxx = -1;
		for (int i = 1; i <= n; i++)//找出最大利益
			maxx = max(f[i], maxx);
		cout << maxx << endl;
			
	}
}

以上都是一维的数组就可以解决 但有些问题需要创建二维数组

二维背包问题!!!有多个约束条件的 需要用

例题一:

【题目描述】
潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119

如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。

你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

【输入】
第一行有2整数m,n(1≤m≤21,1≤n≤79)。它们表示氧,氮各自需要的量。

第二行为整数k(1≤n≤1000)表示气缸的个数。

此后的k行,每行包括ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。

【输出】
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

【输入样例】
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
【输出样例】
249
在这里插入图片描述 约束条件 :体积,气缸的最小值
所以用f【t1】【t2】 t1约束体积,t2约束气缸重量

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int v, u, k, a[1001], b[1001], c[1001];
int f[101][101];
int main()
{
	memset(f, 127, sizeof(f));
	f[0][0] = 0;
	cin >> v >> u >> k;
	for (int i = 1; i <= k; i++)
		cin >> a[i] >> b[i] >> c[i];
	for(int i=1;i<=k;i++)
		for(int j=v;j>=0;j--)
			for (int l = u; l >= 0; l--)
			{
				int t1 = j + a[i]; int t2 = l + b[i];
				if (t1 > v) t1 = v;
				if (t2 > u) t2 = u;
				if (f[t1][t2] > f[j][l] + c[i]) f[t1][t2] = f[j][l] + c[i];
			}
	cout << f[v][u];
}

例题二:

多个约束条件:球的数量,体力值,
用f【a】【b】 a用来约束求得数量
b用来约束体力值

1292:宠物小精灵之收服

时间限制: 1000 ms 内存限制: 65536 KB
提交数: 2888 通过数: 1752

【题目描述】
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。

一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。

我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。

小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。

现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?

【输入】
输入数据的第一行包含三个整数:N(0<N<1000),M(0<M<500),K(0<K<100),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。

之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。

【输出】
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。

输入样例】
10 100 5
7 10
2 40
2 50
1 20
4 20
【输出样例】
3 30
【提示】
样例输入2:
10 100 5
8 110
12 10
20 10
5 200
1 110
样例输出2:
0 100

提示:

对于样例输入1:小智选择:(7,10) (2,40) (1,20) 这样小智一共收服了3个小精灵,皮卡丘受到了70点伤害,剩余100-70=30点体力。所以输出3 30。

对于样例输入2:小智一个小精灵都没法收服,皮卡丘也不会收到任何伤害,所以输出0 100。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m, n,k;
 long long q[1001], s[1001];
long long f[1010][1010];
int max(int a, int b)
{
	return a > b ? a : b;
}
int main()
{
	cin >> n >> m >> k;//球的数量,体力值,野生精灵数量;
		for (int i = 1; i <= k; i++)
		{
			cin >> q[i] >> s[i];
		}
		for(int i=1;i<=k;i++)
			for (int j = n; j >= q[i]; j--)
				for (int h = m; h >=s[i]; h--)
				{
					f[j][h] = max(f[j][h], f[j - q[i]][h - s[i]] + 1);
			    }
		for(int i=1;i<=m;i++)
			if (f[n][i] == f[n][m])
			{
				cout << f[n][m] << " " << m - i; break;
			}
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值