1.题目:数组中部分元素的和
给你一个数组arr,和一个整数aim。如果可以任意选择arr中的数字,能不能累加得到aim,返回true或者false
2.暴力递归
(1)分析
思路和8-3打印字符串的子序列相同,对于每个元素可以选择要或者不要,不断递归,形成一个巨大的树,叶节点就是所有元素判断后的结果。f()函数第一位表示当前位置是第几个元素,第二个参数表示当前和。f(0,0)表示初始位置和是0还没开始记录,f(2,5)表示当前在第二位置,和是5。
结束条件是当前位置超出了数组(每个元素都穷举过了结束),中间过程依旧有一个sum来记录当前和。
(2)核心代码
//当前数 ,当前和,目标
bool isSum(vector<int> v,int i, int sum, int aim)
{
if(i == v.size())
{
return sum == aim;
}
// if(sum > aim) return false;
return isSum(v,i + 1,sum,aim) || isSum(v,i + 1,sum + v[i],aim);
}
3.动态规划
(1)分析
①是否有后效性
对于上述两种情况,需要3,2,不要5和不要3,2,要5,传递出f函数都是f(3,5)说明无后效性。
②dp数组
本题给定的数组,目标aim都不变,只有位置i和sum变化,以i,sum为轴建立二维数组dp
分析:i表示数组中元素的数量 + 1,之所以加一是因为还需要把未开始的(0,0)位置算进去,sum是数组所有元素的和。那么新建的数组dp[i][j]的意思是在第i个位置当前的和是j,整个dp数组包含了所有可能性。对于aim肯定在0 - sum之间,由本题的递归条件,最后的叶子节点就是想要的结果,那么在叶子节点上(最后一列),不在aim的那一列标记为0,表示不符合F,唯独中间的aim列和刚好和为aim表示为1。
再分析任意位置dp[i][j],根据递归条件当前位置依赖于dp[i + 1][j]表示抛弃当前数(j不变)和dp[i][j + v[i]]加上当前数,体现在数组上就是当前行依赖于下面行的两个位置,一个是垂直下方的位置,一个是下方偏移当前数a的位置,两个只要满足一个和为aim就返回1。
下面的目标就是把数组填好,上方位置的依赖于下方的位置,已知最后一行的aim位置为1,其余为0,就可倒推倒数第二行,最后推出0,0位置返回。
(2)核心代码
int isSum2(vector<int>& v, int sum, int aim)
{ //把sum缩减为aim长度,相当于减枝
vector<vector<int> > dp(v.size() + 1,vector<int>(aim + 1,0)); //初始化长度加一因为包括了0,0位置
for (int i = 0; i < dp.size(); i++)
{
dp[i][aim] = 1; //因为依赖垂直的列 aim列值为1,
}
for(int i = dp.size() - 2;i >= 0; i--)
for(int j = aim - 1;j >= 0;j--)
{
dp[i][j] = dp[i + 1][j]; //依赖垂直的列
if(j + v[i] <= aim) //如果另一个偏移的位置不超出数组范围
dp[i][j] = dp[i][j] || dp[i + 1][j + v[i]]; //两个位置有一个满足就可以
}
return dp[0][0];
}
4.完整代码
#include<iostream>
#include<vector>
using namespace std;
//递归 //当前数 ,当前和,目标
bool isSum(vector<int>& v,int i, int sum, int aim)
{
if(i == v.size())
{
return sum == aim;
}
// if(sum > aim) return false;
return isSum(v,i + 1,sum,aim) || isSum(v,i + 1,sum + v[i],aim);
}
//动态规划
int isSum2(vector<int>& v, int sum, int aim)
{ //把sum缩减为aim长度,相当于减枝
vector<vector<int> > dp(v.size() + 1,vector<int>(aim + 1,0)); //初始化长度加一因为包括了0,0位置
for (int i = 0; i < dp.size(); i++)
{
dp[i][aim] = 1; //因为依赖垂直的列 aim列值为1,
}
for(int i = dp.size() - 2;i >= 0; i--)
for(int j = aim - 1;j >= 0;j--)
{
dp[i][j] = dp[i + 1][j]; //依赖垂直的列
if(j + v[i] <= aim) //如果另一个偏移的位置不超出数组范围
dp[i][j] = dp[i][j] || dp[i + 1][j + v[i]]; //两个位置有一个满足就可以
}
return dp[0][0];
}
int main()
{
vector<int>v = {2,1,7,5};
// vector<int>v = {12,14,33,45,66,45,33,2,5,4};
// cout<<isSum(v,0,0,13);
cout<<isSum2(v,0,13);
return 0;
}