题目关键信息:
给定一个数组,将数组分成两个部分,使得这两部分的和的差最小
方法一:动态规划
“数组”,“求最优值”显然这道题可以用动态规划解决,这是0-1背包的变形类题目,求数组两部分的和的差的最小值,假设数组的总和为,部分一的和为,部分二的和为,那么增加,就减小,减小,就增加,,因此背包的最大容量是,只有当不断接近时,他们的差才会最小,于是,我们将问题转化为,在的背包容量中,怎样放置物品才能使尽可能地填满背包。
明确了可选物品的数量是数组的大小,背包的最大容量是后,我们定义数组,明确数组的含义,的含义就是背包容量是j的情况是背包内所装的前个物品的最大价值。
- :背包容量装不下第个礼物,不装下,继承的值
- 背包容量装得下第个物品,在装下与不装下中选择最大值,即
Java版本
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param presentVec int整型一维数组 每个礼物的价值 * @return int整型 */ public int maxPresent (int[] presentVec) { // write code here int sum=0; for(int i=0;i<presentVec.length;i++){ sum+=presentVec[i]; } //背包总容量为sum/2 int m=presentVec.length,n=sum/2; //dp[i][j]表示在背包容量为j的情况下,背包里前i个物品所具有的最大价值 int[][]dp=new int[m+1][n+1]; //遍历背包中的物品 for(int i=1;i<=m;i++){ //遍历背包的容量 for(int j=1;j<=n;j++){ //第i件物品的容量大于背包容量 if(presentVec[i-1]>j){ dp[i][j]=dp[i-1][j]; }else{ //在装入与不装入之间取最大值 dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-presentVec[i-1]]+presentVec[i-1]); } } } return Math.abs(sum-2*dp[m][n]); } }
C++版本
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param presentVec int整型vector 每个礼物的价值 * @return int整型 */ int maxPresent(vector<int>& presentVec) { // write code here int sum=0; for(int i=0;i<presentVec.size();i++){ sum+=presentVec[i]; } //背包总容量为sum/2 int m=presentVec.size(),n=sum/2; //dp[i][j]表示在背包容量为j的情况下,背包里前i个物品所具有的最大价值 vector<vector<int>>dp(m+1,vector<int>(n+1)); //遍历背包中的物品 for(int i=1;i<=m;i++){ //遍历背包的容量 for(int j=1;j<=n;j++){ //第i件物品的容量大于背包容量 if(presentVec[i-1]>j){ dp[i][j]=dp[i-1][j]; }else{ //在装入与不装入之间取最大值 dp[i][j]=max(dp[i-1][j],dp[i-1][j-presentVec[i-1]]+presentVec[i-1]); } } } return abs(sum-2*dp[m][n]); } };
复杂度:
时间复杂度:外层循环次,内层循环次,时间复杂度为
空间复杂度:额外创建的数组大小为,复杂度为
优化后的动态规划
的状态只和的状态有关,我们可以采用滚动数组,将二维数组压缩为一维,具体做法是只保留j这个维度,每一次在遍历第i个物品时,保存着遍历完前i个物品后背包的价值,即还没被新结果覆盖时相当于相当于二维数组中的,为什么这里的j要从后往前遍历呢?因为如果j从前往后遍历,当i不变时,前面已经计算过的会覆盖后面的的意义不等于二维数组中的
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param presentVec int整型一维数组 每个礼物的价值 * @return int整型 */ public int maxPresent (int[] presentVec) { // write code here int sum=0; for(int i=0;i<presentVec.length;i++){ sum+=presentVec[i]; } //背包总容量为sum/2 int m=presentVec.length,n=sum/2; //dp[i][j]表示在背包容量为j的情况下,背包里前i个物品所具有的最大价值 int[]dp=new int[n+1]; //遍历背包中的物品 for(int i=1;i<=m;i++){ //j要从后往前遍历,否则当i不变时,前面已经计算过的dp[j-presentVec[i-1]]会覆盖后面的dp[j-presentVec[i-1]] //dp[j-presentVec[i-1]]的意义不等于二维数组中的dp[i-1][j-presentVec[i-1]],边界条件是背包容量要装得下第i个物品 //遍历第i个物品时,dp[j]保存着遍历完前i个物品后背包的价值,即dp[j]还没被新结果覆盖时相当于dp[i-1][j] for(int j=n;j>=presentVec[i-1];j--){ dp[j]=Math.max(dp[j],dp[j-presentVec[i-1]]+presentVec[i-1]); } } return Math.abs(sum-2*dp[n]); } }
复杂度:
时间复杂度:外层循环次,内层循环次,时间复杂度为
空间复杂度:额外创建的数组大小为,复杂度为