计算青蛙到达河流另一侧所需的最小跳数

时间:2018-08-01 03:38:08

标签: java arrays algorithm performance

我处理下面提供的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));
   }
}

但是,尽管看起来正确性不错,但性能还不够高。代码中是否有错误?如何提高性能?

enter image description here

10 个答案:

答案 0 :(得分:1)

您可以应用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));
 }
}

答案 1 :(得分:1)

在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;
    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;
    while(1)
    {
        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[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;
}

答案 2 :(得分: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 || visited[initPos])
            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;
}

答案 3 :(得分:1)

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 :(得分:0)

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

答案 5 :(得分:0)

我已经将先前的C解决方案翻译为Java,并发现性能得到了改善。

enter image description here

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;
    }
}

答案 6 :(得分:0)

具有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)

答案 7 :(得分:0)

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

答案 8 :(得分:0)

Python 100%的答案。

对我来说,最简单的解决方案是将所有叶片定位在-1的一次跳变内。然后,将这些叶子中的每一个都视为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

答案 9 :(得分:0)

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;
}