[LintCode 797.] 到达一个数字

LintCode 797. 到达一个数字

也是 LeetCode 754. Reach a Number

更新

其实第一份错误代码的内存问题不是占用内存过多,而是 heap-use-after-free。问题出在 auto&& p = q.front(); q.pop(); 使用的引用类型,但是对象随后就被释放掉了。改为 auto p = q.front(); q.pop(); 就变成 TLE 的错误了。


问题描述

你站在一个无穷数轴上的 0 位置。在位置目标上有一个目标。
在每一个动作中,你可以向左或向右。在第n次移动中(从1开始),你行走n步。
返回到达目的地所需的最小步骤数。

样例

样例1

输入: target = 3
输出: 2
解释:
在第一步,我们从0到1。
在第二步,我们从1到3。
样例2

输入: target = 2
输出: 3
解释:
在第一步,我们从0到1。
在第二个步骤中,我们从1到-1。
在第三步,从-1到2。

注意事项

目标将是一个非零的整数范围[-10^9, 10^9]。

解题思路

一开始以为这是个BFS问题,结果提交测试的时候,target=4710915 发生了 Memory Limit Exceeded
这其实是道数学题,通过手动构造最快方案来解决问题。
首先根据对称性,target<0 和 target>0 需要的步数一样多,走的路径相反。我们只需要把tatget转成正数来解决就好。
下面开始构造方案:

  • 首先尽可能往前走,用最少的步数到达或者超过target;
  • 如果正好到达target,则必是最短路径;
  • 如果相差偶数步,则直接翻转前面某一步即可,必是最短路径;
  • 如果相差奇数步,则需要再走1-2步,转成奇数步,再翻转。

我们注意到把第 k 步翻转,则减少的步数是 2k 步。所以如果相差偶数步,只需要翻转第 (sum - target)/2 步即可。

错误代码

BFS 内存占用过多:

class Solution {
public:
    /**
     * @param target: the destination
     * @return: the minimum number of steps
     */
    int reachNumber(int target) {
        // Write your code here
        if (target == 0) return 0;
        queue<pair<int,int>> q;
        q.push(make_pair(0,0));
        while(!q.empty()) {
            auto&& p = q.front();
            q.pop();
            int step = p.second + 1;
            int addr1 = p.first + step;
            if (-1E9 <= addr1 && addr1 <= 1E9) {
                if (addr1 == target) return step;
                else q.push(make_pair(addr1, step));
            }
            int addr2 = p.first - step;
            if (-1E9 <= addr2 && addr2 <= 1E9) {
                if (addr2 == target) return step;
                else q.push(make_pair(addr2, step));
            }
        }
        return -1;
    }
};

换一种写法的BFS,省一半内存也会TLE:

class Solution {
public:
    int reachNumber(int target) {
        if (target == 0) return 0;
        if (target < 0) target = -target;

        int step = 1;
        queue<int> q;
        q.push(0);
        while (!q.empty()) {
            int size = q.size();
            while (size--) {
                int x = q.front();
                q.pop();
                // if (x == target) return step;
                int left = x - step;
                int right = x + step;
                if (left == target || right == target) {
                    return step;
                }
                if (left > -target) q.push(left);
                if (right < target) q.push(right);
            }
            ++step; //
        }
        return -1;
    } // BFS, TLE
};

正确代码

构造法解决:

class Solution {
public:
    /**
     * @param target: the destination
     * @return: the minimum number of steps
     */
    int reachNumber(int target) {
        // Write your code here
        if (target == 0) return 0;
        if (target < 0) target = -target;
        int step = 0;
        int64_t sum = 0;
        while (sum < target) {
            sum += ++step;
        }
        if (sum == target) return step;
        int gap = sum - target;
        if (gap % 2 == 0) return step; // 翻转一个可做到 -2, -4, -6, ...
        if (step % 2 == 0) return step + 1; // 再走一个奇数步,转成偶数差值
        return step + 2; // 走完偶数步才能走奇数步,转成偶数差值
    }
};
原文地址:https://www.cnblogs.com/zhcpku/p/14262280.html