2021-02-02 背包入门

背包

01背包

A - Bone Collector

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
Output
One integer per line representing the maximum of the total value (this number will be less than 231).
Sample Input
1
5 10
1 2 3 4 5
5 4 3 2 1
Sample Output
14

题意

就是告诉你骨头的价值重量,告诉你背包容量求最大骨头价值

题解

典型的01背包问题

#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
const int MAXN=120*120;
const int inf=0x3f3f3f3f;//无穷大 
#define endl "\n"
#define pii pair
int n, m, a[1011], v[1010], d[1010][1010];
int T;
int main(){
     
    cin>>T;
    while(T--){
     
    	memset(d,0,sizeof d);
    	cin>>n>>m;
        for(int i=1 ; i<=n ; i++){
     
        	cin>>a[i];
		}
        for(int i=1 ; i<=n ; i++){
     
        	cin>>v[i];
		}
		for(int i=1 ;i<=n; i++){
     
			for(int j=0;j<=m;j++){
     
				if(j>=v[i]){
     
					d[i][j] = max(d[i-1][j],(d[i-1][j-v[i]]+a[i]));
				}
				else d[i][j] = d[i-1][j];
			}
		}
		cout<<d[n][m]<<endl;
	}
	
	return 0;
}

B - 饭卡

电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有n种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
Input
多组数据。对于每组数据:
第一行为正整数n,表示菜的数量。n<=1000。
第二行包括n个正整数,表示每种菜的价格。价格不超过50。
第三行包括一个正整数m,表示卡上的余额。m<=1000。

n=0表示数据结束。
Output
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
Sample Input
1
50
5
10
1 2 3 2 1 1 2 3 2 1
50
0
Sample Output
-45
32

题解

因为有5元可以买任意菜
所以先将菜的价格排序,最后留5元买最贵的,然后看除去5元的余额还能买多少菜

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=100010;
int T, n, min1, m, v[MAXN], w[MAXN], dp[MAXN];

int main(){
     
    while(cin>>n&&n){
     
    	memset(dp,0, sizeof dp);
		for(int i=1;i<=n;i++) cin>>w[i];
		cin>>m;
		m-=5;//留5块买最贵的东西 
		sort(w+1,w+n+1);
		if(m>=0){
     //卡里的钱大于等于5才能买菜 
			for(int i=1;i<n;i++){
     //最贵的先不买看看能买多少 
				for(int j=m;j>=w[i];j--){
     
					dp[j] = max(dp[j], dp[j-w[i]]+w[i]);
				}
			}
			min1 = m+5-dp[m]-w[n];
			cout<<min1<<endl;
		}
		else cout<<m+5<<endl;
	}
	return 0;
}

01背包回溯求取了哪些物品

E-CD

You have a long drive by car ahead. You have a tape recorder, but unfortunately your best music is on
CDs. You need to have it on tapes so the problem to solve is: you have a tape N minutes long. How
to choose tracks from CD to get most out of tape space and have as short unused space as possible.
Assumptions:
• number of tracks on the CD does not exceed 20
• no track is longer than N minutes
• tracks do not repeat
• length of each track is expressed as an integer number
• N is also integer
Program should find the set of tracks which fills the tape best and print it in the same sequence as
the tracks are stored on the CD
Input
Any number of lines. Each one contains value N, (after space) number of tracks and durations of the
tracks. For example from first line in sample data: N = 5, number of tracks=3, first track lasts for 1
minute, second one 3 minutes, next one 4 minutes
Output
Set of tracks (and durations) which are the correct solutions and string ‘sum:’ and sum of duration
times.
Sample Input
5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2
Sample Output
1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45

题解

就是简单的01背包
下面一部分是用来回溯的
int sum = m;
for (int i = n; i >= 1; i–) {
if (sum >= v[i] && dp[i][sum] == dp[i - 1][sum - v[i]] + v[i]) {
sum -= v[i];
used[i] = 1;
}
}
for(int i=1;i<=n;i++){
if(used[i]==1) cout< }

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=200022;
int T, sum, used[30], n, m, v[30],num, w[30], dp[25][MAXN], a, b, c;
int main(){
     
    while(cin>>m>>n&&n>=0){
     
		memset(dp,0, sizeof dp);
		memset(used,0, sizeof used);
		for(int i=1;i<=n;i++) {
     
			cin>>v[i];
		}
		for(int i=1;i<=n;i++){
     
			for(int j = 1;j<=m;j++){
     
				if(j<v[i]) dp[i][j] = dp[i-1][j];
				else dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i]]+v[i]);
			}
		}
		int sum = m;
		for (int i = n; i >= 1; i--) {
     
			if (sum >= v[i] && dp[i][sum] == dp[i - 1][sum - v[i]] + v[i]) {
     
				sum -= v[i];
				used[i] = 1;
			}
		}
		for(int i=1;i<=n;i++){
     
			if(used[i]==1) cout<<v[i]<<" "; 
		}
		cout<<"sum:"<<dp[n][m]<<endl;
    }
	return 0;
}

F - Big Event in HDU

Nowadays, we all know that Computer College is the biggest department in HDU. But, maybe you don’t know that Computer College had ever been split into Computer College and Software College in 2002.
The splitting is absolutely a big event in HDU! At the same time, it is a trouble thing too. All facilities must go halves. First, all facilities are assessed, and two facilities are thought to be same if they have the same value. It is assumed that there is N (0 Input
Input contains multiple test cases. Each test case starts with a number N (0 < N <= 50 – the total number of different facilities). The next N lines contain an integer V (0 A test case starting with a negative integer terminates input and this test case is not to be processed.
Output
For each case, print one line containing two integers A and B which denote the value of Computer College and Software College will get respectively. A and B should be as equal as possible. At the same time, you should guarantee that A is not less than B.
Sample Input
2
10 1
20 1
3
10 1
20 2
30 1
-1
Sample Output
20 10
40 40

题意

将一部分设备平分到两个学院,要求尽可能总价值相等

题解

按总价值一半为背包容量,取出的值肯定小于等于总价值一半但是已经尽可能了

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=100010;
int T, sum, n, m, v[MAXN],num, w[MAXN], dp[MAXN], a, b, c;

int main(){
     
    while(cin>>n&&n>=0){
     
    	num = 0;
		sum = 0;//总价值,价值即为本题背包的重量 
    	memset(dp,0, sizeof dp);
		for(int i=1;i<=n;i++) {
     
			cin>>a >>c;
			int  k =1;
			while(k<=c){
     
				num++;
				v[num] = a*k;
				sum+=v[num];
	            c-=k;
	            k*=2;
			}
			if(c > 0){
     
				num++;
				v[num] = a*c;
				sum+=v[num];
			}
		}
		for(int i=1;i<=num;i++){
     
			for(int j=sum/2;j>=v[i];j--){
     //求一半总价值能装多少东西,肯定小于等于一半 
				dp[j] = max(dp[j], dp[j-v[i]]+v[i]);
			}
		}
    	cout<<sum-dp[sum/2]<<" "<<dp[sum/2]<<endl;
	}
	return 0;
}

完全背包

相比于01背包,区别是东西可以重复使用
for(int i=1;i<=n;i++){
for(int j=w[i];j<=m;j++){//这里要j++可以重复取
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
}

C - 湫湫系列故事——减肥记I

对于吃货来说,过年最幸福的事就是吃了,没有之一!
  但是对于女生来说,卡路里(热量)是天敌啊!
  资深美女湫湫深谙“胖来如山倒,胖去如抽丝”的道理,所以她希望你能帮忙制定一个食谱,能使她吃得开心的同时,不会制造太多的天敌。

当然,为了方便你制作食谱,湫湫给了你每日食物清单,上面描述了当天她想吃的每种食物能带给她的幸福程度,以及会增加的卡路里量。
Input
  输入包含多组测试用例。
  每组数据以一个整数n开始,表示每天的食物清单有n种食物。
  接下来n行,每行两个整数a和b,其中a表示这种食物可以带给湫湫的幸福值(数值越大,越幸福),b表示湫湫吃这种食物会吸收的卡路里量。
  最后是一个整数m,表示湫湫一天吸收的卡路里不能超过m。

[Technical Specification]
  1. 1 <= n <= 100
  2. 0 <= a,b <= 100000
  3. 1 <= m <= 100000
Output
  对每份清单,输出一个整数,即满足卡路里吸收量的同时,湫湫可获得的最大幸福值。
Sample Input
3
3 3
7 7
9 9
10
5
1 1
5 3
10 3
6 8
7 5
6
Sample Output
10
20

题解

典型的完全背包问题

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=100110;
int T, n, m, v[MAXN], w[MAXN], dp[MAXN];

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

G - 寒冰王座

不死族的巫妖王发工资拉,死亡骑士拿到一张N元的钞票(记住,只有一张钞票),为了防止自己在战斗中频繁的死掉,他决定给自己买一些道具,于是他来到了地精商店前.

死亡骑士:“我要买道具!”

地精商人:“我们这里有三种道具,血瓶150块一个,魔法药200块一个,无敌药水350块一个.”

死亡骑士:“好的,给我一个血瓶.”

说完他掏出那张N元的大钞递给地精商人.

地精商人:“我忘了提醒你了,我们这里没有找客人钱的习惯的,多的钱我们都当小费收了的,嘿嘿.”

死亡骑士:"…"

死亡骑士想,与其把钱当小费送个他还不如自己多买一点道具,反正以后都要买的,早点买了放在家里也好,但是要尽量少让他赚小费.

现在死亡骑士希望你能帮他计算一下,最少他要给地精商人多少小费.
Input
输入数据的第一行是一个整数T(1<=T<=100),代表测试数据的数量.然后是T行测试数据,每个测试数据只包含一个正整数N(1<=N<=10000),N代表死亡骑士手中钞票的面值.

注意:地精商店只有题中描述的三种道具.
Output
对于每组测试数据,请你输出死亡骑士最少要浪费多少钱给地精商人作为小费.
Sample Input
2
900
250
Sample Output
0
50

题解

将三个道具存入数组进行完全背包运算即可

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=100010;
int T, n, min1, m, v[MAXN], w[MAXN], dp[MAXN];

int main(){
     
	cin>>T;
    while(T--){
     
    	cin>>m;
    	memset(dp,0, sizeof dp);
		v[1] = 150; v[2] = 200; v[3] = 350;
		n = 3;
		for(int i = 1;i<=n;i++){
     
			for(int j=v[i];j<=m;j++){
     
				dp[j] = max(dp[j], dp[j-v[i]] + v[i]);
			}
		}
		cout<<m-dp[m]<<endl;
	}
	return 0;
}

多重背包

因为每个物品数量规定了
先建立新数组转化为01背包求解

D - 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

急!灾区的食物依然短缺!
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。
请问:你用有限的资金最多能采购多少公斤粮食呢?

后记:
人生是一个充满了变数的生命过程,天灾、人祸、病痛是我们生命历程中不可预知的威胁。
月有阴晴圆缺,人有旦夕祸福,未来对于我们而言是一个未知数。那么,我们要做的就应该是珍惜现在,感恩生活——
感谢父母,他们给予我们生命,抚养我们成人;
感谢老师,他们授给我们知识,教我们做人
感谢朋友,他们让我们感受到世界的温暖;
感谢对手,他们令我们不断进取、努力。
同样,我们也要感谢痛苦与艰辛带给我们的财富~

Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
Sample Input
1
8 2
2 100 4
4 100 2
Sample Output
400

题解

因为有多个物品
先用二项式重新建立一个数组存储物品,保证每个物品都能取到

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define inf 2x3f3f3f3f
const int MAXN=100011;
int T, n, m, v[MAXN],num, w[MAXN], dp[MAXN], a, b, c;

int main(){
     
	cin>>T;
    while(T--){
     
    	cin>>m>>n;
    	num = 0; 
    	memset(dp,0, sizeof dp);
		for(int i=1;i<=n;i++) {
     
			cin>>a >> b>>c;
			int  k =1;
			while(k<=c){
     
				num++;
				v[num] = b*k;
				w[num] = a*k;
	            c-=k;
	            k*=2;
			}
			if(c > 0){
     
				num++;
				v[num] = b*c;
				w[num] = a*c;
			}
		}
		for(int i=1;i<=num;i++){
     
			for(int j=m;j>=w[i];j--){
     
				dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
			}
		}
    	cout<<dp[m]<<endl;
	}
	return 0;
}

你可能感兴趣的:(2021-02-02 背包入门)