题意:出发点位于数轴上的原点,对于第n步,可选择向左或是向右移动n个单位,求到达target的最小步数。
思考:
最开始没有什么想法,由于数据区间直接覆盖整个int类型,bfs什么的肯定不可能;
尝试贪心,但是没有任何数学依据。易证target具有对称性,因此不考虑起手往反方向跳的情况(贪心的想法),一直向右直到接近target;
因为第k步与第k+1步相差1,可以认为对于一段长度为1的路径,有cost(1)=2;
那么如果现在距离target为d,答案是否就是2*d呢?
很容易想到反例,不过解决这个问题似乎只要多跳一步然后看看两个2d和2d'哪个更小就行了。
尝试着交了一发,结果n=4就不行了:
- -1+2+3=4,ans=3
- 1+2-3+4=4,ans=4
区别在于第一步,答案是后跳的,因此贪心一定是错误的。
很容易将原问题转化为数学问题,一个等式,把1,2,3,...,n和target放进去,求最小的n能让等式成立。
高中的时候似乎有见过类似的东西,但是年代过于久远,也许数学竞赛的同学能够很快找到解决方法吧。
现在考虑整个流程:
- 0+1=1
- 1+2=3
- 3+3=6
- ...
- 0-1=-1
- -1+2=1
- 1+3=4√
我们可以发现寻找正解是需要在某些步骤进行逆运算的,而一次逆运算产生的差值与正运算相比为2n,也就是说我们可以通过将贪心结果减去一些2n对答案进行修正。由于n是递增的(包含了连续的数字),不需要考虑修正产生的越界问题以及不足以修正的问题。
于是,对于贪心后结果position与target差值为偶数的情况,我们通过修改运算符号即可得到正解,而此时步数不用改变;
对于奇数的情况,则取决于当前的n值,我们想要做的是将当前的奇数转化为偶数,那么当前n值是奇数的话,只需要再走一步,就得到了上述情况,也就是ans+=1;
当前n为偶数,则无法改变position与target差值的奇偶性,此时需要多走两步,即ans+=2;
因为此前我们采用贪心的策略,默认为到达target的最短路径不会存在其它最优解,大概可以证明上述算法的正确性..
以下代码部分,不过最后的奇偶部分为了方便好看并没有做成+1+2这些形式,最好按自己的理解完成代码编写:
class Solution {
public:
int reachNumber(int target) {
int ans = 0;
if (target < 0) target *= -1;
if (target == 0) return ans;
int pos = 0;
while(pos < target){
++ans;
pos += ans;
}//O(sqrt(n)), for more powerful data, please do solve the formula to get the answer
if (pos > target){
int delta = pos - target;
if (delta % 2 == 1){
if (ans % 2 == 0)
++ans;
else ans += 2;
}
}
return ans;
}
};