【问题标题】:Count the minimum number of jumps required for a frog to get to the other side of a river计算青蛙到达河对岸所需的最少跳跃次数
【发布时间】:2019-01-08 12:53:40
【问题描述】:

我正在处理下面提供的 Codility 问题,

斐波那契数列使用以下递归公式定义:

F(0) = 0
F(1) = 1
F(M) = F(M - 1) + F(M - 2) if M >= 2

一只小青蛙想去河的另一边。青蛙最初位于河的一岸(位置-1)并想到达另一岸(位置N)。青蛙可以跳过任何距离 F(K),其中 F(K) 是第 K 个斐波那契数。幸好河边有很多树叶,青蛙可以在树叶之间跳跃,但只能在N位置的河岸方向。

河流上的叶子表示在一个由 N 个整数组成的数组 A 中。数组 A 的连续元素表示河流上从 0 到 N - 1 的连续位置。数组 A 只包含 0 和/或 1:

0 表示没有叶子的位置; 1 表示包含叶子的位置。 目标是计算青蛙可以到达河对岸(从位置 -1 到位置 N)的最小跳跃次数。青蛙可以在 -1 和 N 位置(河岸)之间以及每个包含叶子的位置之间跳跃。

例如,考虑数组 A,这样:

A[0] = 0
A[1] = 0
A[2] = 0
A[3] = 1
A[4] = 1
A[5] = 0
A[6] = 1
A[7] = 0
A[8] = 0
A[9] = 0
A[10] = 0

青蛙可以跳三个长度为 F(5) = 5、F(3) = 2 和 F(5) = 5。

写一个函数:

class Solution { public int solution(int[] A); }

给定一个由 N 个整数组成的数组 A,返回青蛙可以到达河对岸的最小跳跃次数。如果青蛙无法到达河的另一边,该函数应返回 -1。

例如,给定:

A[0] = 0
A[1] = 0
A[2] = 0
A[3] = 1
A[4] = 1
A[5] = 0
A[6] = 1
A[7] = 0
A[8] = 0
A[9] = 0
A[10] = 0

该函数应返回 3,如上所述。

假设:

N是[0..100,000]范围内的整数; 数组 A 的每个元素都是一个整数,可以具有以下值之一:0、1。 复杂性:

预计最坏情况时间复杂度为O(N*log(N)); 预期的最坏情况空间复杂度为O(N)(不包括输入参数所需的存储空间)。

我写了以下解决方案,

class Solution {
    private class Jump {
        int position;
        int number;

        public int getPosition() {
            return position;
        }

        public int getNumber() {
            return number;
        }

        public Jump(int pos, int number) {
            this.position = pos;
            this.number = number;
        }
    }

    public int solution(int[] A) {

        int N = A.length;

        List<Integer> fibs = getFibonacciNumbers(N + 1);

        Stack<Jump> jumps = new Stack<>();
        jumps.push(new Jump(-1, 0));

        boolean[] visited = new boolean[N];

        while (!jumps.isEmpty()) {

            Jump jump = jumps.pop();

            int position = jump.getPosition();
            int number = jump.getNumber();

            for (int fib : fibs) {

                if (position + fib > N) {
                    break;
                } else if (position + fib == N) {
                    return number + 1;
                } else if (!visited[position + fib] && A[position + fib] == 1) {

                    visited[position + fib] = true;
                    jumps.add(new Jump(position + fib, number + 1));
                }
            }
        }

        return -1;
    }


    private List<Integer> getFibonacciNumbers(int N) {

        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < 2; i++) {
            list.add(i);
        }

        int i = 2;

        while (list.get(list.size() - 1) <= N) {

            list.add(i, (list.get(i - 1) + list.get(i - 2)));
            i++;
        }

        for (i = 0; i < 2; i++) {
            list.remove(i);
        }

        return list;
    }




    public static void main(String[] args) {

    int[] A = new int[11];

    A[0] = 0;
    A[1] = 0;
    A[2] = 0;
    A[3] = 1;
    A[4] = 1;
    A[5] = 0;
    A[6] = 1;
    A[7] = 0;
    A[8] = 0;
    A[9] = 0;
    A[10] = 0;

    System.out.println(solution(A));
   }
}

然而,虽然正确性看起来不错,但性能还不够高。代码中是否存在错误,如何提高性能?

【问题讨论】:

  • 也许你不需要一个列表,只需要一个 for 循环。也许
  • Flopshot:如果你没有什么可贡献的,那就考虑什么都不贡献。在这里活泼是没有意义的。这是一个写得很好的问题。我认为这不是主题,但可以肯定的是,OP 付出了相当多的努力,不应该被嘲笑。
  • @MarcosVasconcelos 你是什么意思?
  • @GhostCat 我反对,因为问题是关于如何提高性能。我不需要任何帮助来改进设计等
  • @flopshot 对不起,你错了。 trivia 对你来说可能是其他人的真正问题。乱扔垃圾的是无数糟糕写的 no-attempt-drop-me-codez 问题。当您真正关心这些时,请花一些时间来提高您的声誉,这样您就可以参与对内容的投票,可能是关闭/关闭/......无论投票。这就是贡献的含义,而不是对写得好的问题不屑一顾。

标签: java arrays algorithm performance


【解决方案1】:

通过简单的 BFS 获得 100%:

public class Jump {
    int pos;
    int move;
    public Jump(int pos, int move) {
        this.pos = pos;
        this.move = move;
    }
}

public int solution(int[] A) {

    int n = A.length;
    List < Integer > fibs = fibArray(n + 1);
    Queue < Jump > positions = new LinkedList < Jump > ();
    boolean[] visited = new boolean[n + 1];

    if (A.length <= 2)
        return 1;

    for (int i = 0; i < fibs.size(); i++) {
        int initPos = fibs.get(i) - 1;
        if (A[initPos] == 0)
            continue;
        positions.add(new Jump(initPos, 1));
        visited[initPos] = true;
    }

    while (!positions.isEmpty()) {
        Jump jump = positions.remove();
        for (int j = fibs.size() - 1; j >= 0; j--) {
            int nextPos = jump.pos + fibs.get(j);
            if (nextPos == n)
                return jump.move + 1;
            else if (nextPos < n && A[nextPos] == 1 && !visited[nextPos]) {
                positions.add(new Jump(nextPos, jump.move + 1));
                visited[nextPos] = true;
            }
        }
    }


    return -1;
}


private List < Integer > fibArray(int n) {
    List < Integer > fibs = new ArrayList < > ();
    fibs.add(1);
    fibs.add(2);
    while (fibs.get(fibs.size() - 1) + fibs.get(fibs.size() - 2) <= n) {
        fibs.add(fibs.get(fibs.size() - 1) + fibs.get(fibs.size() - 2));
    }
    return fibs;
}

【讨论】:

【解决方案2】:

您可以应用knapsack 算法来解决这个问题。 在我的解决方案中,我预先计算了斐波那契数。并应用背包算法求解。它包含重复的代码,没有太多时间来重构它。相同代码的在线ide在repl

import java.util.*;
class Main {

public static int solution(int[] A) {

    int N = A.length;
    int inf=1000000;
    int[] fibs={1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025};
    int[] moves = new int[N+1];
     for(int i=0; i<=N; i++){
        moves[i]=inf;
     }
    for(int i=0; i<fibs.length; i++){
        if(fibs[i]-1<N && A[fibs[i]-1]==1){
            moves[ fibs[i]-1 ] = 1;
        }
        if(fibs[i]-1==N){
           moves[N] = 1;
        }
    }

    for(int i=0; i<N; i++){
        if(A[i]==1)
        for(int j=0; j<fibs.length; j++){
            if(i-fibs[j]>=0 && moves[i-fibs[j]]!=inf && moves[i]>moves[i-fibs[j]]+1){
                moves[i]=moves[i-fibs[j]]+1;
            }                
        }
         System.out.println(i + " => " + moves[i]);
    }

     for(int i=N; i<=N; i++){
        for(int j=0; j<fibs.length; j++){
            if(i-fibs[j]>=0 && moves[i-fibs[j]]!=inf && moves[i]>moves[i-fibs[j]]+1){
                moves[i]=moves[i-fibs[j]]+1;
            }                
        }
         System.out.println(i + " => " + moves[i]);
    }

    if(moves[N]==inf) return -1;
    return moves[N];
}

public static void main(String[] args) {

int[] A = new int[4];

A[0] = 0;
A[1] = 0;
A[2] = 0;
A[3] = 0;
System.out.println(solution(A));
 }
}

【讨论】:

  • 它确实将性能提高到了 66%,但仍然不能令人满意
  • 你确定吗?我得到了 100% 的正确性和复杂性
【解决方案3】:

Javascript 100%

function solution(A) {
    function fibonacciUntilNumber(n) {
        const fib = [0,1];
        while (true) {
            let newFib = fib[fib.length - 1] + fib[fib.length - 2];
            if (newFib > n) {
                break;
            }
            fib.push(newFib);
        }
        return fib.slice(2);
    }
    A.push(1);
    const fibSet = fibonacciUntilNumber(A.length);
    if (fibSet.includes(A.length)) return 1;
    const reachable = Array.from({length: A.length}, () => -1);

    fibSet.forEach(jump => {
        if (A[jump - 1] === 1) {
            reachable[jump - 1] = 1;
        }
    })

    for (let index = 0; index < A.length; index++) {
        if (A[index] === 0 || reachable[index] > 0) {
            continue;
        }
        let minValue = 100005;
        for (let jump of fibSet) {
            let previousIndex = index - jump;
            if (previousIndex < 0) {
                break;
            }
            if (reachable[previousIndex] > 0 && minValue > reachable[previousIndex]) {
                minValue = reachable[previousIndex];
            }
        }
        if (minValue !== 100005) {
            reachable[index] = minValue + 1;
        }
    }
    return reachable[A.length - 1];
}

【讨论】:

    【解决方案4】:

    Python 100% 答案。

    对我来说,最简单的解决方案是将所有叶子都定位在 -1 的一个 fib jump 内。然后将这些叶子中的每一个视为 index[0] 并从那里找到所有跳转。 每个世代或跳跃都记录在一个集合中,直到世代包含 len(A) 或找不到更多的跳跃。

    def gen_fib(n):
        fn = [0,1]
        i = 2
        s = 2
        while s < n:
            s = fn[i-2] + fn[i-1]
            fn.append(s)
            i+=1
        return fn
    
    def new_paths(A, n, last_pos, fn):
        """
        Given an array A of len n.
        From index last_pos which numbers in fn jump to a leaf?
        returns list: set of indexes with leaves.
        """
        paths = []
        for f in fn:
            new_pos = last_pos + f
            if new_pos == n or (new_pos < n and A[new_pos]):
                paths.append(new_pos)
        return path
    
    def solution(A):
        n = len(A)
        if n < 3:
            return 1
    
        # A.append(1) # mark final jump
        fn = sorted(gen_fib(100000)[2:]) # Fib numbers with 0, 1, 1, 2..  clipped to just 1, 2..
        # print(fn)
        paths = set([-1]) # locate all the leaves that are one fib jump from the start position.
    
        jump = 1
        while True:
            # Considering each of the previous jump positions - How many leaves from there are one fib jump away
            paths =  set([idx for pos in paths for idx in new_paths(A, n, pos, fn)])
    
            # no new jumps means game over!
            if not paths:
                break
    
            # If there was a result in the new jumps record that
            if n in paths:
                return jump
                
            jump += 1
    
        return -1
    

    https://app.codility.com/demo/results/training4GQV8Y-9ES/

    https://github.com/niall-oc/things/blob/master/codility/fib_frog.py

    【讨论】:

      【解决方案5】:

      在 C 中得到 100% 的解决方案。

      typedef struct state {
          int pos;
          int step;
      }state;
      int solution(int A[], int N) {
      
          int f1 = 0;
          int f2 = 1;
          int count = 2;
          // precalculating count of maximum possible fibonacci numbers to allocate array in next loop. since this is C language we do not have flexible dynamic structure as in C++
          while(1)
          {
              int f3 =  f2 + f1;
              if(f3 > N)
                  break;
              f1 = f2;
              f2 = f3;
              ++count;
          }
          int fib[count+1];
          fib[0] = 0;
          fib[1] = 1;
          int i = 2;
          // calculating fibonacci numbers in array
          while(1)
          {
              fib[i] =  fib[i-1] + fib[i-2];
              if(fib[i] > N)
                  break;
              ++i;
          }
          // reversing the fibonacci numbers because we need to create minumum jump counts with bigger jumps
          for(int j = 0, k = count; j < count/2; j++,k--)
          {
              int t = fib[j];
              fib[j] = fib[k];
              fib[k] = t;
          }
          state q[N];
          int front = 0 ;
          int rear = 0;
          q[0].pos = -1;
          q[0].step = 0;
          int que_s = 1;
          while(que_s > 0)
          {
              state s =  q[front];
              front++;
              que_s--;
              for(int i = 0; i <= count; i++)
              {
                  int nextpo = s.pos + fib[i];
                  if(nextpo == N)
                  {
                      return s.step+1;
                  }
                  else if(nextpo > N || nextpo < 0 || A[nextpo] == 0){
                 
                      continue;  
                  }
                  else
                  {
                      q[++rear].pos = nextpo;
                      q[rear].step = s.step + 1;
                      que_s++;
                      A[nextpo] = 0;
                  }
              }
          }
          return -1;
      }
      

      【讨论】:

      • 我将用 Java 编写它并稍后进行测试。如果性能看起来更好,会更新你。
      【解决方案6】:

      //100% 关于 codility 动态编程解决方案。 https://app.codility.com/demo/results/training7WSQJW-WTX/

      class Solution {
        public int solution(int[] A) {
          int n = A.length + 1;
          int dp[] = new int[n];
          for(int i=0;i<n;i++) {
              dp[i] = -1;
          }
          int f[] = new int[100005];
          f[0] = 1;
          f[1] = 1;
          for(int i=2;i<100005;i++) {
              f[i] = f[i - 1] + f[i - 2];
          }
          for(int i=-1;i<n;i++) {
              if(i == -1 || dp[i] > 0) {
                  for(int j=0;i+f[j] <n;j++) {
                      if(i + f[j] == n -1  || A[i+f[j]] == 1) {
                          if(i == -1) {
                              dp[i + f[j]] = 1;
                          } else if(dp[i + f[j]] == -1) {
                              dp[i + f[j]] = dp[i] + 1;
                          } else {
                              dp[i + f[j]] = Math.min(dp[i + f[j]], dp[i] + 1);
                          }
                      }
                  }
                  }
          }
      
          return dp[n - 1];
      }
      }
      

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案7】:

      Ruby 100% 解决方案

      def solution(a)
        f = 2.step.inject([1,2]) {|acc,e| acc[e] = acc[e-1] + acc[e-2]; break(acc) if acc[e] > a.size + 1;acc }.reverse
        mins = []
      
        (a.size + 1).times do |i|
          next mins[i] = -1 if i < a.size && a[i] == 0
      
          mins[i] = f.inject(nil) do |min, j|
              k = i - j
              next min if k < -1
              break 1 if k == -1
              next min if mins[k] < 0
              [mins[k] + 1, min || Float::INFINITY].min
          end || -1
        end
      
        mins[a.size]
      end
      

      【讨论】:

        【解决方案8】:

        我已经将之前的C解决方案翻译成Java,发现性能有所提升。

        import java.util.*;
        
        
        class Solution {
        
                private static class State {
        
                int pos;
                int step;
        
                public State(int pos, int step) {
        
                    this.pos = pos;
                    this.step = step;
                }
            }
        
        
            public static int solution(int A[]) {
        
                int N = A.length;
        
                int f1 = 0;
                int f2 = 1;
        
                int count = 2;
        
                while (true) {
        
                    int f3 = f2 + f1;
        
                    if (f3 > N) {
                        break;
                    }
        
                    f1 = f2;
                    f2 = f3;
        
                    ++count;
                }
        
        
                int[] fib = new int[count + 1];
        
                fib[0] = 0;
                fib[1] = 1;
        
                int i = 2;
        
                while (true) {
        
                    fib[i] = fib[i - 1] + fib[i - 2];
        
                    if (fib[i] > N) {
                        break;
                    }
        
                    ++i;
                }
        
                for (int j = 0, k = count; j < count / 2; j++, k--) {
        
                    int t = fib[j];
        
                    fib[j] = fib[k];
                    fib[k] = t;
                }
        
                State[] q = new State[N];
        
                for (int j = 0; j < N; j++) {
        
                    q[j] = new State(-1,0);
                }
        
                int front = 0;
                int rear = 0;
        
                // q[0].pos = -1;
                // q[0].step = 0;
        
                int que_s = 1;
        
                while (que_s > 0) {
        
                    State s = q[front];
        
                    front++;
                    que_s--;
        
                    for (i = 0; i <= count; i++) {
        
                        int nextpo = s.pos + fib[i];
        
                        if (nextpo == N) {
                            return s.step + 1;
                        }
        
                        //
                        else if (nextpo > N || nextpo < 0 || A[nextpo] == 0) {
                            continue;
                        }
        
                        //
                        else {
        
                            q[++rear].pos = nextpo;
                            q[rear].step = s.step + 1;
        
                            que_s++;
        
                            A[nextpo] = 0;
                        }
                    }
                }
        
                return -1;
            }
        }
        

        【讨论】:

          【解决方案9】:

          100% 的 JavaScript。 灵感来自here

          function solution(A) {
            const createFibs = n => {
              const fibs = Array(n + 2).fill(null)
              fibs[1] = 1
              for (let i = 2; i < n + 1; i++) {
                fibs[i] = fibs[i - 1] + fibs[i - 2]
              }
              return fibs
            }
            const createJumps = (A, fibs) => {
              const jumps = Array(A.length + 1).fill(null)
              let prev = null
              for (i = 2; i < fibs.length; i++) {
                const j = -1 + fibs[i]
                if (j > A.length) break
                if (j === A.length || A[j] === 1) {
                  jumps[j] = 1
                  if (prev === null) prev = j
                }
              }
              if (prev === null) {
                jumps[A.length] = -1
                return jumps
              }
              while (prev < A.length) {
                for (let i = 2; i < fibs.length; i++) {
                  const j = prev + fibs[i]
                  if (j > A.length) break
                  if (j === A.length || A[j] === 1) {
                    const x = jumps[prev] + 1
                    const y = jumps[j]
                    jumps[j] = y === null ? x : Math.min(y, x)
                  }
                }
                prev++
                while (prev < A.length) {
                  if (jumps[prev] !== null) break
                  prev++
                }
              }
              if (jumps[A.length] === null) jumps[A.length] = -1
              return jumps
            }
            const fibs = createFibs(26)
            const jumps = createJumps(A, fibs)
            return jumps[A.length]
          }
          
          const A = [0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0]
          console.log(A)
          const s = solution(A)
          console.log(s)
          

          【讨论】:

            【解决方案10】:

            您应该使用队列而不是堆栈。这是广度优先搜索的一种形式,您的代码需要访问首先添加到队列中的节点以获得最小距离。 堆栈使用后进先出机制来移除项目,而队列使用先进先出机制。 我复制并粘贴了您的确切代码,但使用了队列而不是堆栈,我得到了 100% 的编码。

            【讨论】:

            • 如果您可以使用示例代码实现该场景并将您的答案发布为对社区有用的内容,那总是会更好。
            【解决方案11】:

            100% C++ solution

            更多答案my github

            灵感来自here

            解决方案1:自下而上,使用动态编程算法(将计算值存储在数组中)

            vector<int> getFibonacciArrayMax(int MaxNum) {
                if (MaxNum == 0)
                    return vector<int>(1, 0);
                vector<int> fib(2, 0);
                fib[1] = 1;
                for (int i = 2; fib[fib.size()-1] + fib[fib.size() - 2] <= MaxNum; i++)
                    fib.push_back(fib[i - 1] + fib[i - 2]);
                return fib;
            }
            
            int solution(vector<int>& A) {
                int N = A.size();
                A.push_back(1);
                N++;
                vector<int> f = getFibonacciArrayMax(N);
                const int oo = 1'000'000;
                vector<int> moves(N, oo);
                
                for (auto i : f)
                    if (i - 1 >= 0 && A[i-1])
                        moves[i-1] = 1;
                
                for (int pos = 0; pos < N; pos++) {
                    if (A[pos] == 0)
                        continue;
            
                    for (int i = f.size()-1; i >= 0; i--) {
                        if (pos + f[i] < N && A[pos + f[i]]) {
                            moves[pos + f[i]] = min(moves[pos]+1, moves[pos + f[i]]);
                        }
                    }
                }
                if (moves[N - 1] != oo) {
                    return moves[N - 1];
                }
                return -1;
            }
            

            解决方案2:自上而下使用set容器:

            
            #include <set>
            
            int solution2(vector<int>& A) {
                int N = A.size();
            
                vector<int> fib = getFibonacciArrayMax(N);
            
                set<int> positions;
                positions.insert(N);
                for (int jumps = 1; ; jumps++)
                {
                    set<int> new_positions;
                    for (int pos : positions)
                    {
                        for (int f : fib)
                        {
                            // return jumps if we reach to the start point
                            if (pos - (f - 1) == 0)
                                return jumps;
                            
                            int prev_pos = pos - f;
                            
                            // we do not need to calculate bigger jumps.
                            if (prev_pos < 0)
                                break;
                            
                            if (prev_pos < A.size() && A[prev_pos])
                                new_positions.insert(prev_pos);
                        }
                    }
                    if (new_positions.size() == 0)
                        return -1;
                    positions = new_positions;
                }
            
                return -1;
            }
            

            【讨论】:

              猜你喜欢
              • 2021-06-09
              • 2014-06-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-09-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多