46.携带研究材料
题目描述
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。
输入描述
第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。
第二行包含 M 个正整数,代表每种研究材料的所占空间。
第三行包含 M 个正整数,代表每种研究材料的价值。
输出描述
输出一个整数,代表小明能够携带的研究材料的最大价值。
输入示例
6 1
2 2 3 1 5 2
2 3 1 5 4 3
输出示例
5
提示信息
小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。
数据范围:
1 <= N <= 5000
1 <= M <= 5000
研究材料占用空间和价值都小于等于 1000
思路:
这里运用了动态规划,首先我们设置dp[i][j]
为前i+1个物品的最大价值数,设置时我们要注意进行初始化,所以我们去weight[0]为基准,大于weight[0]的我们赋值value[0]小于的我们赋值0,然后我们进行判断,如果没有装满,我们就一直往下装,如果装满了,我们就进行判断是换成下一个的价值大,还是自身的价值大,最后我们返回答案,就可以求解。
解答:
第一种方法(二维数组)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int m, n;
scanf("%d %d", &m, &n);
int* weight = (int*)malloc(sizeof(int) * m);
int* value = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; ++i)
{
scanf("%d", &weight[i]);
}
for (int i = 0; i < m; i++)
{
scanf("%d", &value[i]);
}
int** dp = (int**)malloc(sizeof(int*) * m);
for (int i = 0; i < m; i++)
{
dp[i] = (int*)malloc(sizeof(int) * (n + 1));
}
for (int j = 0; j <= n; j++)
{
dp[0][j] = (j >= weight[0]) ? value[0] : 0;
}
for (int i = 1; i < m; i++)
{
for (int j = 0; j <= n; j++)
{
if (j < weight[i])
{
dp[i][j] = dp[i - 1][j];
}
else
{
dp[i][j] = fmax(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
printf("%d\n", dp[m - 1][n]);
return 0;
}
第二种方法(滚动数组(一维))
思路:
我们直接压缩,设置dp[n]为前n个空间内的最大价值,然后我们后面的步骤就像上面的方法一样,但是很明显节约了内存也节省了时间。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int m, n;
scanf("%d %d", &m, &n);
int* weight = (int*)malloc(sizeof(int) * m);
int* value = (int*)malloc(sizeof(int) * m);
for (int i = 0; i < m; ++i)
{
scanf("%d", &weight[i]);
}
for (int i = 0; i < m; i++)
{
scanf("%d", &value[i]);
}
int* dp = malloc(sizeof(int)*(n+1));
for(int i = 0;i <=n;i++)
{
dp[i] = 0;
}
for (int i = 0; i < m; i++)
{
for (int j = n; j >= weight[i]; j--)
{
dp[j] = fmax(dp[j], dp[j-weight[i]] + value[i]);
}
}
printf("%d\n", dp[n]);
return 0;
}
416.分割等和子集
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
思路:
这一道题的dp[i]是表示是否能得到i的值,所以我们先算出数组和,看他是否为偶数,不为偶数我们就去掉它,是的话我们就继续往下做,我们把总值除以2,用target表示,然后我们用两个for循环来实现算出dp[target]时的值为多少,然后用dp[target]去与target进行比较,最终得出答案。
解答:
bool canPartition(int* nums, int numsSize) {
int sum = 0;
for(int i = 0;i < numsSize;i++)
{
sum += nums[i];
}
int target = sum / 2;
int* dp = malloc(sizeof(int)*(target+1));
for(int i = 0;i < target;i++)
{
dp[i] = 0;
}
if(sum % 2 != 0)
{
return false;
}
for(int i = 0;i < numsSize;i++)
{
for(int j = target;j >= nums[i];j--)
{
dp[j]=fmax(dp[j],dp[j-nums[i]]+nums[i]);
}
}
if(dp[target] == target)
{
return true;
}
return false;
}
反思
今天的动态规划都有一定难度,需要进行复习。