动态规划
416. 分割等和子集
题意:给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
实例:
思路:本题是一个典型的0-1背包问题。首先要想让数组和是两个子数组和组成的就需要数组和是偶数,因此我们可以先判断数组和是否为偶数,不是则直接返回false。另外我们创建一个数组dp,大小给我数组和的一半sum+1,初始化都为0。由于每个元素只能使用一次,因此我们需要从后往前遍历背包,将每个数组放入背包,背包的序号就是背包的大小。当我们遍历完全部元素后,如果dp[sum]等于sum,就说明可以有两个子数组构成;否则,无法构成
C++代码:
bool canPartition(vector<int>& nums) {
int sum = 0;
for (auto e : nums)
{
sum += e;
}
if (sum % 2 != 0)
{
return false;
}
sum /= 2;
vector<int> dp(sum + 1, 0);
for (int i = 0; i<nums.size(); i++)//遍历物品
{
for (int j = sum; j >= nums[i]; j--)//遍历背包
{
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return dp[sum] == sum ? true : false;
}
1049. 最后一块石头的重量 II
题意:有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
- 如果 x == y,那么两块石头都会被完全粉碎;
- 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
实例:
思路:本题我们使用的和分割等和子集类似,就是将我们需要相撞的石头平均分成两部分,这样它们相撞就能得到最少的石头。因此,在代码方面几乎一模一样,就是在返回时,我们需要返回数组和减去两倍的平均石堆
C++代码:
int lastStoneWeightII(vector<int>& stones) {
int sum=0;
for(auto e:stones)
{
sum+=e;
}//23
int tmp=sum;//保存数组的和
sum/=2;//11
vector<int> dp(sum+1,0);//这里开始累加,让数组和的一半容量的背包尽可能满
for(int i=0;i<stones.size();i++)
{
for(int j=sum;j>=stones[i];j--)
{
dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return tmp-2*dp[sum];//最后输出数组和-2*一半背包容量的最大容量
}
494. 目标和
题意:给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
实例:
思路:本题也是0-1背包的经典问题,我们利用数学的思想:我们给出正数的元素总和left和需要减去数的元素总和right,此时整个数组的总和是:
left+right=整个数组,又由left-right=target,将两个方程进行替换得到一个关于left的方程 2*left=整个数组+target,因此left=(整个数组+target)/2。此时本题就转换为了数组中正数组成left的组成方式,即我们创建的数组dp[i]就表示i位置有多少种方式组成。
我们以target=5为例,当元素为1时,我们就有dp[4]种方式可以凑成dp[5];当元素为2时,我们就有dp[3]种方式可以凑成dp[5].....以此类推,我们的dp[5]就是由dp[4],dp[3],dp[2],dp[1],dp[0]这么些种方式组成。
C++代码:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0;
for(auto e:nums)
{
sum+=e;
}
sum+=target;
if(sum<0||sum%2!=0)
{
return 0;
}
sum/=2;
vector<int> dp(sum+1,0);
dp[0]=1;
for(int i=0;i<nums.size();i++)
{
for(int j=sum;j>=nums[i];j--)
{
dp[j]+=dp[j-nums[i]];
}
}
return dp[sum];
}