分组背包+二维费用背包-笔记+题目

目录

A.  NASA的食物计划

B.  潜水员

C.  分组背包

D.  混合背包

E.  愚公移山

F.  布置房间


A.  NASA的食物计划

题目背景
NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证,在遇到这类航天问题时,解决方法也许只能让航天员出仓维修,但是多次的维修会消耗航天员大量的能量,因此NASA便想设计一种食品方案,让体积和承重有限的条件下多装载一些高卡路里的食物.

题目描述
航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.

输入格式
第一行 两个数 体积最大值(<400)和质量最大值(<400)

第二行 一个数 食品总数N(<50).

第三行-第3+N行

每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)

输出格式
一个数 所能达到的最大卡路里(int范围内)

输入样例

320 350

4

160 40 120

80 110 240

220 70 310

40 400 220

输出样例

550

#include<bits/stdc++.h>
using namespace std;
int a[55],b[55],c[55];
int dp[505][505];
int main(){
	int v,w,n;
	cin>>v>>w>>n;
	for(int i=1;i<=n;i++)
 		cin>>a[i]>>b[i]>>c[i];
	for(int i=1;i<=n;i++){
 		for(int j=v;j>=a[i];j--){
  			for(int k=w;k>=b[i];k--)
   				dp[j][k]=max(dp[j][k],dp[j-a[i]][k-b[i]]+c[i]);
 		}
	}
	cout<<dp[v][w]<<endl;
	return 0;
}

B.  潜水员

时间:1s   空间:128M

题目描述:

现有一些带两种气体(氧气和氮气)的气缸,潜水员为了完成工作需要特定数量的氧和氮,每个气缸都有自身的重量和两种气体的含量。请问潜水员所需携带气缸的最小总重量是多少?

例如:有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≤k≤1000)表示气缸的个数。

此后的k行,每行包括ai,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

#include <bits/stdc++.h>
using namespace std;
int m, n, k;
int a[1005], b[1005], c[1005];
int dp[1005][30][85];
int main(){
	scanf("%d%d", &m, &n);
	scanf("%d", &k);
	for(int i = 1; i <= k; ++i){
		scanf("%d%d%d", &a[i], &b[i], &c[i]);
	}
	memset(dp, 0x3f, sizeof dp);
	dp[0][0][0] = 0;
	for(int i = 1; i <= k; ++i){
		for(int j = m; j >= 0; --j){
			for(int g = n; g >= 0; --g){	
				dp[i][j][g]=min(dp[i-1][j][g],dp[i-1][max(0,j-a[i])][max(0,g-b[i])]+c[i]);
			}
		}
	}
	printf("%d\n", dp[k][m][n]);
	return 0;
}

Runtime Error:

#include <bits/stdc++.h>
using namespace std;
int m, n, k;
int a[1005], b[1005], c[1005];
int dp[30][85][1005];
int main(){
	scanf("%d%d", &m, &n);
	scanf("%d", &k);
	for(int i = 1; i <= k; ++i){
		scanf("%d%d%d", &a[i], &b[i], &c[i]);
	}
	memset(dp, 0x3f, sizeof dp);
	dp[0][0][0] = 0;
	for(int i = 1; i <= k; ++i){
		for(int j = m; j >= 0; --j){
			for(int g = n; g >= 0; --g){	
				dp[i][j][g]=min(dp[i-1][j][g],dp[i-1][max(0,j-a[i])][max(0,g-b[i])]+c[i]);
			}
		}
	}
	printf("%d\n", dp[k][m][n]);
	return 0;
}

C.  分组背包

时间:1s   空间:128M

题目描述:

  一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

输入格式:

第1行:三个整数,V(背包容量,V<=200),N(物品数量,N<=30)和T(最大组号,T<=10);
第2..N+1行:每行三个整数Wi,Ci,P,表示每个物品的重量,价值,所属组号。

输出格式:

仅一行,一个数,表示最大总价值。

样例输入:

10 6 3

2 1 1

3 3 1

4 8 2

6 9 2

2 8 3

3 9 3

样例输出:

20

#include<bits/stdc++.h>
using namespace std;
int w[10000],q[10000],p[1000][1000],dp[10000];
int main(){
	int s;
	int v,n,t;
	cin>>v>>n>>t;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>q[i]>>s; 
		p[s][0]++;
		p[s][p[s][0]]=i;
	}
	for(int i=1;i<=t;i++){
		for(int j=v;j>=0;j--){
			for(int k=1;k<=p[i][0];k++){
				if(j>=w[p[i][k]]){
					dp[j]=max(dp[j],dp[j-w[p[i][k]]]+q[p[i][k]]);
				}
			}
		}
	}
	cout<<dp[v];
	return 0;
}

D.  混合背包

时间:1s   空间:128M

题目描述:

  一个旅行者有一个最多能用V公斤的背包,现在有n件物品,它们的重量分别是W1,W2,...,Wn,它们的价值分别为C1,C2,...,Cn。有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

输入格式:

第一行:二个整数,V(背包容量,V<=200),N(物品数量,N<=30);
第2..N+1行:每行三个整数Wi,Ci,Pi,前两个整数分别表示每个物品的重量,价值,第三个整数若为0,则说明此物品可以购买无数件,若为其他数字,则为此物品可购买的最多件数 (0≤Pi≤20) 。

输出格式:

仅一行,一个数,表示最大总价值。

样例输入:

10 3

2  1  0

3  3  1

4  5  4

样例输出:

11

提示:

选第一件物品1件和第三件物品2件。

#include <bits/stdc++.h>
using namespace std;
int v,n;
int w[35],c[35],p[35],f[205];
int main() {
    cin>>v>>n;
    for(int i=1; i<=n; i++) {
        cin>>w[i]>>c[i]>>p[i];
    }
    for(int i=1; i<=n; i++) {
        if(p[i]==0) {
            for(int j=w[i]; j<=v; j++) {
                f[j]=max(f[j-w[i]]+c[i],f[j]);
            }
        } else if(p[i]==1) {
            for(int j=v; j>=w[i]; j--) {
                f[j]=max(f[j-w[i]]+c[i],f[j]);
            }
        } else {
            for(int j=v; j>=w[i]; j--) {
                for(int k=0; k<=p[i]; k++) {
                    if(j-k*w[i]<0) {
                        break;
                    }
                    f[j]=max(f[j-k*w[i]]+k*c[i],f[j]);
                }
            }
        }
    }
    cout<<f[v];
}

E.  愚公移山

题目描述

【版权说明】

本题为改编题。

【问题描述】

愚公移山的故事相信大家都听过,愚公日复一日的劳作,终于还需要再搬走至少体积为 m 的石头这条路就畅通了,已知每块的体积和把它移走需要的体力分别为 vi 和 wi。愚公已经移山移了这么长时间了,他也很累了,他还剩下的体力为c。

输入格式

输入文件的第一行是三个整数:m、n、c。(1≤m,n,c≤10000)

接下来 n 行,每行两个整数:分别为每块石头的体积 vi 和移走需要的体力 wi。(1≤vi,wi≤1000)

输出格式

输出文件只有一行,如果愚公能把山移完,则输出他把山移完剩下的最大的体力,否则输出 "Impossible"(不带引号)。

输入输出样例

输入 #1

100 2 10

50 5

50 5

输出 #1

0

输入 #2

10 2 1

50 5

10 2

输出 #2

Impossible

#include <bits/stdc++.h>
using namespace std;
int m, n, c;
int v[10005], w[10005];
int dp[10005];
int main(){
	cin>>m>>n>>c;
	for(int i = 1; i <= n; i++){
		cin>>v[i]>>w[i];
	}
	memset(dp, 0x3f, sizeof dp);
	dp[0] = 0;
	for(int i = 1; i <= n; ++i){
		for(int j = m; j >=0; j--){
			dp[j]=min(dp[j],dp[max(0,j-v[i])]+w[i]);
		}
	}
	int x=c-dp[m];
	if(x>=0){
		cout<<x;
	}
	else{
		cout<<"Impossible";
	}
	return 0;
}

F.  布置房间

题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件 附件

电脑 打印机,扫描仪

书柜 图书

书桌 台灯,文具

工作椅 无

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有0个、1个或2个附件。附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的N元。于是,他把每件物品规定了一个重要度,分为5等:用整数1~5表示,第5等最重要。他还从因特网上查到了每件物品的价格(都是10元的整数倍)。他希望在不超过N元(可以等于N元)的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第j件物品的价格为v[j],重要度为w[j],共选中了k件物品,编号依次为j1,j2,……,jk,则所求的总和为:

v[j1]*w[j1]+v[j2]*w[j2]+ …+v[jk]*w[jk]。(其中*为乘号)

请你帮助金明设计一个满足要求的购物单。

输入格式

两个正整数,用一个空格隔开:

N m (其中N(<32000)表示总钱数,m(<60)为希望购买物品的个数。)

从第2行到第m+1行,第j行给出了编号为j-1的物品的基本数据,每行有3个非负整数

v w q

(其中v表示该物品的价格(v<10000),w表示该物品的重要度(1~5),q表示该物品是主件还是附件。如果q=0,表示该物品为主件,如果q>0,表示该物品为附件,q是所属主件的编号)

输出格式

只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<200000)

样例输入

1000 5

800 2 0

400 5 1

300 5 1

400 3 0

500 2 0

样例输出

2200

时空限制

1s,512M

#include<bits/stdc++.h>
using namespace std;
int n,m,f[40000],g[40000];
struct ff{
	int p,v,t;
}a[100];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
	    scanf("%d%d%d",&a[i].p,&a[i].v,&a[i].t); 
		a[i].v*=a[i].p;
	}
    for(int i=1;i<=m;i++){
	    if(!a[i].t){
	        for(int j=1;j<a[i].p;j++){
	        	g[j]=0;	
			}
	        for(int j=a[i].p;j<=n;j++){
	        	g[j]=f[j-a[i].p]+a[i].v;
			}
	        for(int j=1;j<=m;j++){
	        	if(a[j].t==i){
	        		for(int k=n;k>=a[i].p+a[j].p;k--){
	        			g[k]=max(g[k],g[k-a[j].p]+a[j].v);	
					}
				}
			}
	        for(int j=a[i].p;j<=n;j++){
	        	f[j]=max(f[j],g[j]);
			}
	    }
	}
    cout<<f[n];
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值