Algorithm:494. 目标和
Review: 在责怪别人之前,先检查自己的代码
Tip/Tech: 归并排序
Share: 隐形天花板
Algorithm
494. 目标和
https://leetcode-cn.com/problems/target-sum/

这里其实用动态规划的思想来求解其实非常的复杂。需要用到推导的方式,其实网上有很多这种非常优质解决方法了,最重要的是看你能否理解,我这里直接引用网上的大神的优质的解法方式来进行解答了,排版不行,也许你看到第四步会不理解,那么在在回来看我的文章也好。
https://blog.youkuaiyun.com/hit0803107/article/details/54894227
【问题分析】
1、该问题求解数组中数字只和等于目标值的方案个数,每个数字的符号可以为正或负(减整数等于加负数)。
2、该问题和矩阵链乘很相似,是典型的动态规划问题
3、举例说明: nums = {1,2,3,4,5}, target=3, 一种可行的方案是+1-2+3-4+5 = 3
该方案中数组元素可以分为两组,一组是数字符号为正(P={1,3,5}),另一组数字符号为负(N={2,4})
因此: sum(1,3,5) - sum(2,4) = target
sum(1,3,5) - sum(2,4) + sum(1,3,5) + sum(2,4) = target + sum(1,3,5) + sum(2,4)
2sum(1,3,5) = target + sum(1,3,5) + sum(2,4)
2sum§ = target + sum(nums)
sum§ = (target + sum(nums)) / 2
由于target和sum(nums)是固定值,因此原始问题转化为求解nums中子集的和等于sum§的方案个数问题
4、求解nums中子集合只和为sum§的方案个数(nums中所有元素都是非负)
该问题可以通过动态规划算法求解
举例说明:给定集合nums={1,2,3,4,5}, 求解子集,使子集中元素之和等于9 = new_target = sum§ = (target+sum(nums))/2
定义dp[10]数组, dp[10] = {1,0,0,0,0,0,0,0,0,0}
dp[i]表示子集合元素之和等于当前目标值的方案个数, 当前目标值等于9减去当前元素值
当前元素等于1时,dp[9] = dp[9] + dp[9-1]
dp[8] = dp[8] + dp[8-1]
…
dp[1] = dp[1] + dp[1-1]
当前元素等于2时,dp[9] = dp[9] + dp[9-2]
dp[8] = dp[8] + dp[8-2]
…
dp[2] = dp[2] + dp[2-2]
当前元素等于3时,dp[9] = dp[9] + dp[9-3]
dp[8] = dp[8] + dp[8-3]
…
dp[3] = dp[3] + dp[3-3]
当前元素等于4时,
…
当前元素等于5时,
…
dp[5] = dp[5] + dp[5-5]
最后返回dp[9]即是所求的解
其实这里到了前三步是非常好理解的,但是到了第四步,很多人可能就会看不懂了,其实这里因为省略了一个步奏,而导致的没看明白,因为这里默认了大家都明白一个子集求和的最佳做法。
因为题目已经可以被看成是求 原来的数组中有多少的子集可以用来求新的目标的。这道题目最原始的做法是根据二维数组来求解的。dp(i, j )表示前i 个数中和为 j 的方案数,则若j >= a[i], dp (i, j)= dp(i -1,j)+ dp(i - 1,j - a[i] );否则,dp(i ,j) = dp(i -1, j)。
**但是最佳的做法是啥呢?**因为这里用了二维的数组,其实最佳的做法用的一维的数组。其实一维的数组就的第二重循环就不能从小到大了,需要从大到小,也就有了这样一个递推公式:dp[j] = dp[j] + dp[j - num[i]]
下面的是代码:
class Solution {
public int findTargetSumWays(int[] nums, int S) {
int len = nums.length;
int tempSum = 0;
for (int i = 0; i < len; ++i) {
tempSum += nums[i];
}
int newTarget = (tempSum + S) / 2;
if (S > newTarget || (tempSum + S) % 2 == 1) {
return 0;
}
// 二维数组的解法,但是有个测试样例怎么都通过不了,有点郁闷了。。
// int[][] dp = new int[len][newTarget + 1];
// dp[0][0] =1;
// if (nums[0] <= newTarget) {
// dp[0][nums[0]] = 1;
// }
// for (int i = 1; i < len; ++i) {
// dp[i][0] = dp[i - 1][0] + dp[i - 1][0];
// }
// for (int i = 1; i < len; ++i) {
// for (int j = 1; j <= newTarget; ++j) {
// if (nums[i] <= j) {
// dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j];
// } else {
// dp[i][j] = dp[i - 1][j];
// }
// }
// }
// 一维数组的解法
int[] dp = new int[newTarget + 1];
dp[0] = 1;
for (int i = 0; i < len; ++i) {
for (int j = newTarget; j >= nums[i]; --j) {
dp[j] = dp[j] + dp[j - nums[i]];
}
}
return dp[newTarget];
}
}
Review
Check Your Code First before Looking to Blame Others
在责怪别人之前,先检查自己的代码。
其实看完这篇文章之后,我发现,这里已经不能翻译成别人了,应该翻译成别的因素。
就是我们在编写代码的过程中会遇到各种各样的问题,有的时候,我们会怀疑编译器,怀疑工具,怀疑同事写的代码除了毛病,但是其实最大的问题还是我们自己。我们一定要先检查我们自己的代码,确保自己的代码没错才行。
So before you rush to blame the compiler, remember Sherlock Holmes’ advice, “Once you eliminate the impossible, whatever remains, no matter how improbable, must be the truth,” and prefer it to Dirk Gently’s, “Once you eliminate the improbable, whatever remains, no matter how impossible, must be the truth.”
记住夏洛克福尔摩斯的名言,一旦你排除了所有不可能因素,那么无论剩下的多么难以置信,那就是真相。
Tip/Tech
复习了关于归并排序的代码。
Share
https://www.eugenewei.com/blog/2018/5/21/invisible-asymptotes/?from=singlemessage&isappinstalled=0
隐形天花板
本文的作者分析了目前国外的各大公司的各种隐形的天花板,其中第一个例子就是,对于亚马逊这种大型的电商公司来说,它的隐形的天花板是啥?额。。。是邮费。老外在抠门这点上和我们没啥区别,就算邮费加上商品的售价还是要比零售商店的便宜,但是他们依然会觉得商品的邮费好贵啊。导致他们的购买欲望很低,后来亚马逊通过了各种各样的手段来解决这样的问题,比如超过了多少钱就可以包邮,比如你购买了会员就可以免运费。种种的手段来包邮。
当然这里面还穿插了几个jeff的决策,比如规模比现有的盈利更重要?额??怎么让我想起了雷军???哈哈哈
庆幸的是,Jeff决定跳过测试,直接执行。把增长放在第一位,长期再来解决商业变现的问题,这在科技公司中并不少见。但比起单位经济模型更清晰的零售型业务,这种模式在社交网络中更容易被接受。

博客包含四部分内容。Algorithm部分用动态规划求解494.目标和问题;Review强调编写代码遇问题先检查自身;Tip/Tech复习归并排序代码;Share分析国外公司隐形天花板,如亚马逊邮费问题及应对策略。
2339

被折叠的 条评论
为什么被折叠?



