56. 携带矿石资源(卡码网第八期模拟笔试)
题目描述
你是一名宇航员,即将前往一个遥远的行星。在这个行星上,有许多不同类型的矿石资源,每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球,但你的宇航舱有一定的容量限制。
给定一个宇航舱,最大容量为 C。现在有 N 种不同类型的矿石,每种矿石有一个重量 w[i],一个价值 v[i],以及最多 k[i] 个可用。不同类型的矿石在地球上的市场价值不同。你需要计算如何在不超过宇航舱容量的情况下,最大化你所能获取的总价值。
输入
输入共包括四行,第一行包含两个整数 C 和 N,分别表示宇航舱的容量和矿石的种类数量。
接下来的三行,每行包含 N 个正整数。具体如下:
第二行包含 N 个整数,表示 N 种矿石的重量。
第三行包含 N 个整数,表示 N 种矿石的价格。
第四行包含 N 个整数,表示 N 种矿石的可用数量上限。
输出
输出一个整数,代表获取的最大价值。
样例输入
10 3
1 3 4
15 20 30
2 3 2
样例输出
90
提示
数据范围:
1 <= C <= 10000;
1 <= N <= 10000;
1 <= w[i], v[i], k[i] <= 10000;
题解1(C++版本)
// 多重背包之二进制优化版本
#include<bits/stdc++.h>
using namespace std;
const int N = 100010; //踩坑点:使用二进制优化时,把N定义为10010,报运行错误,然后我把N定义为100010就通过了
int n, w, C[N], W[N], K[N], dp[N],C2[N], W2[N], K2[N],cnt = 0;
/*
10 3
1 3 4
15 20 30
2 3 2
90
7 3
2 3 1
3 5 2
12 15 3
12
*/
int main(){
scanf("%d%d", &w, &n);
for(int i = 1; i <= n; i++)scanf("%d", &W[i]);
for(int i = 1; i <= n; i++)scanf("%d", &C[i]);
for(int i = 1; i <= n; i++)scanf("%d", &K[i]);
for(int i = 1; i <= n; i++){
for(int k = 1; k <= K[i]; k <<= 1){
C2[++cnt] = k * C[i];
W2[cnt] = k * W[i];
K[i] -= k;
}
if(K[i]){
C2[++cnt] = K[i] * C[i];
W2[cnt] = K[i] * W[i];
}
}
//采用01背包的求解方式
for(int i = 1; i <= cnt; i++){ //把n个背包合并成cnt个背包
for(int j = w; j >= W2[i]; j--){
dp[j] = max(dp[j], dp[j - W2[i]] + C2[i]);
}
}
printf("%d\n", dp[w]);
return 0;
}
// 多重背包之朴素版本
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 10010; //这里把N定义为10010就能通过,我很奇怪
int n, w, cnt = 0;
int C[N], W[N], K[N], dp[N];
int main(){
scanf("%d%d", &w, &n);
for(int i = 1; i <= n; i++)scanf("%d", &W[i]);
for(int i = 1; i <= n; i++)scanf("%d", &C[i]);
for(int i = 1; i <= n; i++)scanf("%d", &K[i]);
for(int i = 1; i <= n; i++){
for(int j = w; j >= W[i]; j--){
for(int k = 1; k <= K[i] && (j - k*W[i] >= 0); k++){
dp[j] = max(dp[j], dp[j - k * W[i]] + k * C[i]);
}
}
}
printf("%d\n", dp[w]);
return 0;
}