Python Fib Frog
from collections import deque


def fib_frog(a: list[int]) -> int:
    size = len(a)

    fib = [0, 1]
    i = 1
    while fib[i] <= size:
        i += 1
        fib.append(fib[i - 1] + fib[i - 2])

    queue = deque([(-1, 0)])
    visited = [False] * size

    while queue:
        idx, jumps = queue.popleft()
        for f in range(len(fib) - 1, 1, -1):
            new_idx = idx + fib[f]
            if new_idx == size:
                return jumps + 1
            if new_idx > size or visited[new_idx] or a[new_idx] == 0:
                continue
            if a[new_idx] == 1:
                visited[new_idx] = True
                queue.append((new_idx, jumps + 1))

    return -1

This precomputes Fibonacci jumps, then uses a breadth-first search to find the shortest valid path across the river.

Rust Fib Frog
use std::collections::VecDeque;

fn fib_frog(a: &[i64]) -> i64 {
    let size = a.len() as i64;

    let mut fib = vec![0i64, 1];
    let mut i = 1;
    while fib[i] <= size {
        i += 1;
        fib.push(fib[i - 1] + fib[i - 2]);
    }

    let mut paths: VecDeque<(i64, i64)> = VecDeque::new();
    paths.push_back((-1, 0));

    let mut steps = vec![false; size as usize];

    while let Some((idx, jmp)) = paths.pop_front() {
        for f in (2..fib.len()).rev() {
            let next_idx = idx + fib[f];
            if next_idx == size {
                return jmp + 1;
            }
            if next_idx > size || steps[next_idx as usize] || a[next_idx as usize] == 0 {
                continue;
            }
            if a[next_idx as usize] == 1 {
                steps[next_idx as usize] = true;
                paths.push_back((next_idx, jmp + 1));
            }
        }
    }

    -1
}

This precomputes Fibonacci jumps, then uses a breadth-first search to find the shortest valid path across the river.

TypeScript Fib Frog
function fibFrog(a: number[]): number {
  const size = a.length;

  const fib: number[] = [0, 1];
  for (let i = 1; fib[i] <= size; ) {
    i++;
    fib[i] = fib[i - 1] + fib[i - 2];
  }

  type Path = { idx: number; jmp: number };
  const paths: Path[] = [{ idx: -1, jmp: 0 }];
  const steps: boolean[] = new Array(size).fill(false);

  while (paths.length > 0) {
    const path = paths.shift() as Path;
    for (let i = fib.length - 1; i >= 2; i--) {
      const idx = path.idx + fib[i];
      if (idx === size) {
        return path.jmp + 1;
      }
      if (idx > size || steps[idx] || a[idx] === 0) {
        continue;
      }
      if (a[idx] === 1) {
        steps[idx] = true;
        paths.push({ idx, jmp: path.jmp + 1 });
      }
    }
  }

  return -1;
}

This precomputes Fibonacci jumps, then uses a breadth-first search to find the shortest valid path across the river.

Bash Fish
fish() {
    local -n _a="$1"
    local -n _b="$2"
    local _size=${#_a[@]}
    local _dead=0
    local -a _stack=()
    local _i
    for ((_i = 0; _i < _size; _i++)); do
        if (( _b[_i] == 1 )); then
            _stack+=("${_a[_i]}")
        elif (( ${#_stack[@]} > 0 )); then
            while (( ${#_stack[@]} > 0 )); do
                ((_dead++))
                local _top=${_stack[-1]}
                if (( _a[_i] > _top )); then
                    unset '_stack[-1]'
                    _stack=("${_stack[@]}")
                else
                    break
                fi
            done
        fi
    done
    echo $(( _size - _dead ))
}

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

C++ Fish
#include <vector>

int fish(const std::vector<int>& a, const std::vector<int>& b)
{
    int size = static_cast<int>(a.size());
    int dead = 0;
    std::vector<int> downstream;

    for (int i = 0; i < size; ++i) {
        if (b[i] == 1) {
            downstream.push_back(a[i]);
        } else if (!downstream.empty()) {
            while (!downstream.empty()) {
                ++dead;
                if (a[i] > downstream.back()) {
                    downstream.pop_back();
                } else {
                    break;
                }
            }
        }
    }

    return size - dead;
}

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

C# Fish
static int Fish(int[] a, int[] b)
{
    var size = a.Length;
    var dead = 0;
    var fish = new Stack<int>();

    for (int i = 0; i < size; i++)
    {
        if (b[i] == 1)
        {
            fish.Push(a[i]);
        }
        else
        {
            while (fish.Count > 0)
            {
                dead++;
                if (a[i] > fish.Peek())
                {
                    fish.Pop();
                }
                else
                {
                    break;
                }
            }
        }
    }

    return size - dead;
}

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

Elixir Fish
defmodule Fish do
  def fish(a, b) do
    size = length(a)

    {_stack, dead} =
      a
      |> Enum.zip(b)
      |> Enum.reduce({[], 0}, fn {size_i, dir_i}, {stack, dead} ->
        if dir_i == 1 do
          {[size_i | stack], dead}
        else
          fight(size_i, stack, dead)
        end
      end)

    size - dead
  end

  defp fight(_size_i, [], dead), do: {[], dead}

  defp fight(size_i, [top | rest] = stack, dead) do
    if size_i > top do
      fight(size_i, rest, dead + 1)
    else
      {stack, dead + 1}
    end
  end
end

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

Erlang Fish
-module(fish).
-export([fish/2]).

fish(A, B) ->
    Pairs = lists:zip(A, B),
    {_, Dead} = lists:foldl(fun({Ai, Bi}, {Stack, D}) ->
        case Bi of
            1 -> {[Ai | Stack], D};
            _ -> fight(Ai, Stack, D)
        end
    end, {[], 0}, Pairs),
    length(A) - Dead.

fight(_Ai, [], Dead) ->
    {[], Dead};
fight(Ai, [Top | Rest], Dead) ->
    Dead1 = Dead + 1,
    case Ai > Top of
        true -> fight(Ai, Rest, Dead1);
        false -> {[Top | Rest], Dead1}
    end.

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

Go Fish
func fish(a, b []int) int {
	size := len(a)
	dead := 0
	downstream := make([]int, 0, size)

	for i := 0; i < size; i++ {
		if b[i] == 1 {
			downstream = append(downstream, a[i])
		} else {
			for len(downstream) > 0 {
				dead++
				if a[i] > downstream[len(downstream)-1] {
					downstream = downstream[:len(downstream)-1]
				} else {
					break
				}
			}
		}
	}

	return size - dead
}

This uses a stack for downstream fish and resolves fights only when opposite directions meet.

Haskell Fish
fish :: [Int] -> [Int] -> Int
fish a b = size - dead
  where
    size      = length a
    (dead, _) = foldl step (0, []) (zip a b)

    step (d, stack) (ai, bi)
      | bi == 1   = (d, ai : stack)
      | otherwise = fight ai stack d

    fight _  []             d = (d, [])
    fight ai (top : tops) d
      | ai > top  = fight ai tops (d + 1)
      | otherwise = (d + 1, top : tops)

This uses a stack for downstream fish and resolves fights only when opposite directions meet.