17

Four men have to cross a bridge at night.Any party who crosses, either one or two men, must carry the flashlight with them. The flashlight must be walked back and forth; it cannot be thrown, etc. Each man walks at a different speed. One takes 1 minute to cross, another 2 minutes, another 5, and the last 10 minutes. If two men cross together, they must walk at the slower man's pace. There are no tricks--the men all start on the same side, the flashlight cannot shine a long distance, no one can be carried, etc.

And the question is What's the fastest they can all get across. I am basically looking for some generalized approach to these kind of problem. I was told by my friend, that this can be solved by Fibonacci series, but the solution does not work for all.

Please note this is not a home work.

Ganesh M
  • 3,666
  • 8
  • 27
  • 25
  • 1
    No .. this is not a home work .. I am not a student .. – Ganesh M Jul 17 '09 at 16:04
  • 1
    Kyahaha, I was asked this during an interview, but it was constrained further by saying it was at night, very dark and the flashlight battery can only last 17 minutes. – Jimmy Chandra Jul 17 '09 at 16:10
  • 2
    @ Jimmy Chandra - doesn't that give away the answer? – Matthew Jones Jul 17 '09 at 16:11
  • 1
    I had an interview like that once. They provided a ride and for part of the way I had to wear a blindfold. – Smandoli Jul 17 '09 at 16:13
  • @Jimmy Chandra, that is a simpler version of the question, here you have the answer, you just have to prove it right. The original question is relatively difficult – Vivek Sharma Jul 17 '09 at 16:17
  • @Matt: not really, it tells you the constraint, but you have to find out how to do it. In that sense, they are trying to see how you think. They did ask you to explain your thought pattern while you are trying to solve it, which I think is a good interview technique. – Jimmy Chandra Jul 17 '09 at 16:17
  • 6
    When does the wolf attack the sheep? (http://www.xkcd.com/589/) – Nathan Koop Jul 17 '09 at 16:18

12 Answers12

19

There is an entire PDF (alternate link) that solves the general case of this problem (in a formal proof).

Matthew Jones
  • 25,644
  • 17
  • 102
  • 155
18

17 minutes - this is a classic MS question.

1,2 => 2 minutes passed.
1 retuns => 3 minutes passed.
5,10 => 13 minutes passed.
2 returns => 15 minutes passed.
1,2 => 17 minute passed.

In general the largest problem / slowest people should always be put together, and sufficient trips of the fastest made to be able to bring the light back each time without using a slow resource.

Andrew
  • 26,629
  • 5
  • 63
  • 86
14

I would solve this problem by placing a fake job ad on Dice.com, and then asking this question in the interviews until someone gets it right.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • 3
    I think this was the fastest down-vote I've ever gotten. Awesome! – MusiGenesis Jul 17 '09 at 16:10
  • 1
    +1. *O(n)* complexity (with *n* the number of interviews, not the number of bridge crossers, unfortunately ;)) – Stephan202 Jul 17 '09 at 16:18
  • @Stephan202: increasing n (interviewees) is a good thing for me, because I look like I'm accomplishing something at work, plus in this economy it's easy to extort candidates and make them pay for lunch and stuff. – MusiGenesis Jul 17 '09 at 16:21
7

As per Wikipedia

The puzzle is known to have appeared as early as 1981, in the book Super Strategies For Puzzles and Games. In this version of the puzzle, A, B, C and D take 5, 10, 20, and 25 minutes, respectively, to cross, and the time limit is 60 minutes

This question was however popularized after its appearance in the book "How Would You Move Mount Fuji?"

the question can be generalized for N people with varying individual time taken to cross the bridge.

The below program works for a generic N no of people and their times.

class Program
{
    public static int TotalTime(List<int> band, int n)
    {
        if (n < 3)
        {
            return band[n - 1];
        }
        else if (n == 3)
        {
            return band[0] + band[1] + band[2];
        }
        else
        {
            int temp1 = band[n - 1] + band[0] + band[n - 2] + band[0];
            int temp2 = band[1] + band[0] + band[n - 1] + band[1];

            if (temp1 < temp2)
            {
                return temp1 + TotalTime(band, n - 2);
            }
            else if (temp2 < temp1)
            {
                return temp2 + TotalTime(band, n - 2);
            }
            else
            {
                return temp2 + TotalTime(band, n - 2);
            }
        }
    }

    static void Main(string[] args)
    {
        // change the no of people crossing the bridge
        // add or remove corresponding time to the list
        int n = 4; 

        List<int> band = new List<int>() { 1, 2, 5, 10 };

        band.Sort();

        Console.WriteLine("The total time taken to cross the bridge is: " + Program.TotalTime(band, n));
        Console.ReadLine();
    }
}

OUTPUT:

The total time taken to cross the bridge is: 17

For,

int n = 5; 
List<int> band = new List<int>() { 1, 2, 5, 10, 12 };

OUTPUT:

The total time taken to cross the bridge is: 25

For,

int n = 4; 
List<int> band = new List<int>() { 5, 10, 20, 25 };

OUTPUT The total time taken to cross the bridge is: 60

Chinmay Lokesh
  • 671
  • 1
  • 7
  • 9
4

Here's the response in ruby:

@values = [1, 2, 5, 10]
# @values = [1, 2, 5, 10, 20, 25, 30, 35, 40]
@values.sort!
@position = @values.map { |v| :first }
@total = 0

def send_people(first, second)
  first_time = @values[first]
  second_time = @values[second]

  @position[first] = :second
  @position[second] = :second

  p "crossing #{first_time} and #{second_time}"

  first_time > second_time ? first_time : second_time
end

def send_lowest
  value = nil
  @values.each_with_index do |v, i|
    if @position[i] == :second
      value = v
      @position[i] = :first
      break
    end
  end

  p "return #{value}"
  return value
end


def highest_two
  first = nil
  second = nil

  first_arr = @position - [:second]

  if (first_arr.length % 2) == 0
    @values.each_with_index do |v, i|
      if @position[i] == :first
        first = i unless first
        second = i if !second && i != first
      end

      break if first && second
    end
  else
    @values.reverse.each_with_index do |v, i|
      real_index = @values.length - i - 1
      if @position[real_index] == :first
        first = real_index unless first
        second = real_index if !second && real_index != first
      end

      break if first && second
    end
  end

  return first, second
end

#we first send the first two
@total += send_people(0, 1)
#then we get the lowest one from there
@total += send_lowest
#we loop through the rest with highest 2 always being sent
while @position.include?(:first)
  first, second = highest_two
  @total += send_people(first, second)
  @total += send_lowest if @position.include?(:first)
end

p "Total time: #{@total}"
Roc Khalil
  • 1,365
  • 6
  • 22
4

Another Ruby implementation inspired by @roc-khalil 's solution

@values = [1,2,5,10]
# @values = [1,2,5,10,20,25]
@left = @values.sort
@right = []
@total_time = 0

def trace(moving)
  puts moving
  puts "State: #{@left} #{@right}"
  puts "Time: #{@total_time}"
  puts "-------------------------"
end

# move right the fastest two
def move_fastest_right!
  fastest_two = @left.shift(2)
  @right = @right + fastest_two
  @right = @right.sort
  @total_time += fastest_two.max
  trace "Moving right: #{fastest_two}"
end

# move left the fastest runner
def move_fastest_left!
  fastest_one = @right.shift
  @left << fastest_one
  @left.sort!
  @total_time += fastest_one
  trace "Moving left: #{fastest_one}"
end

# move right the slowest two
def move_slowest_right!
  slowest_two = @left.pop(2)
  @right = @right + slowest_two
  @right = @right.sort
  @total_time += slowest_two.max
  trace "Moving right: #{slowest_two}"
end

def iterate!
  move_fastest_right!
  return if @left.length == 0

  move_fastest_left!
  move_slowest_right!
  return if @left.length == 0

  move_fastest_left!
end

puts "State: #{@left} #{@right}"
puts "-------------------------"
while @left.length > 0
  iterate!
end

Output:

State: [1, 2, 5, 10] []
-------------------------
Moving right: [1, 2]
State: [5, 10] [1, 2]
Time: 2
-------------------------
Moving left: 1
State: [1, 5, 10] [2]
Time: 3
-------------------------
Moving right: [5, 10]
State: [1] [2, 5, 10]
Time: 13
-------------------------
Moving left: 2
State: [1, 2] [5, 10]
Time: 15
-------------------------
Moving right: [1, 2]
State: [] [1, 2, 5, 10]
Time: 17
-------------------------
Abdo
  • 13,549
  • 10
  • 79
  • 98
2

An exhaustive search of all possibilities is simple with such a small problem space. Breadth or depth first would work. It is a simple CS problem.

I prefer the missionary and cannibal problems myself

Tim
  • 20,184
  • 24
  • 117
  • 214
1

17 -- a very common question

-> 1-2 = 2
<- 2 = 2
-> 5,10 = 10 (none of them has to return)
<- 1 = 1
-> 1,2 = 2

all on the other side
total = 2+2+10+1+2 = 17

usually people get it as 19 in the first try

Vivek Sharma
  • 3,794
  • 7
  • 38
  • 48
  • Opps. someone already did it, i type slow :D. anyways, the solution is standard and its a very standard puzzle. – Vivek Sharma Jul 17 '09 at 16:07
  • Even I know the solution. But looking for a generalized solution for these kind of problems. Anyway good try. – Ganesh M Jul 17 '09 at 16:10
1

Considering there will be 2 sides, side 1 and side 2, and N number of people should cross from side 1 to side 2. The logic to cross the bridge by a limit of L number of people would be -

Step 1 : Move L number of the fastest members from side 1 to side 2

Step 2 : Bring back the fastest person back from Side 2 to Side 1

Step 3 : Move L number of slowest members from side 1 to side 2

Step 4 : Bring back the fastest person among the ones present in Side 2

Repeat these steps until you will be left with no one in Side 1, either at the end of step 2 or at the end of step 4.

A code in C# for n number of people, with just 2 persons at a time is here. This will intake N number of people, which can be specified in runtime. It will then accept person name and time taken, for N people. The output also specifies the iteration of the lowest time possible.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RiverCrossing_Problem
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, int> Side1 = new Dictionary<string, int>();
            Dictionary<string, int> Side2 = new Dictionary<string, int>();

            Console.WriteLine("Enter number of persons");
            int n = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("Enter the name and time taken by each");

            for(int a =0; a<n; a++)
            {
                string tempname = Console.ReadLine();
                int temptime = Convert.ToInt32(Console.ReadLine());
                Side1.Add(tempname, temptime);
            }

            Console.WriteLine("Shortest time and logic:");
            int totaltime = 0;
            int i = 1;
            do
            {
                KeyValuePair<string, int> low1, low2, high1, high2;
                if (i % 2 == 1)
                {
                    LowestTwo(Side1, out low1, out low2);
                    Console.WriteLine("{0} and {1} goes from side 1 to side 2, time taken = {2}", low1.Key, low2.Key, low2.Value);
                    Side1.Remove(low2.Key);
                    Side1.Remove(low1.Key);
                    Side2.Add(low2.Key, low2.Value);
                    Side2.Add(low1.Key, low1.Value);
                    totaltime += low2.Value;

                    low1 = LowestOne(Side2);
                    Console.WriteLine("{0} comes back to side 1, time taken = {1}", low1.Key, low1.Value);
                    totaltime += low1.Value;
                    Side1.Add(low1.Key, low1.Value);
                    Side2.Remove(low1.Key);
                    i++;
                }
                else
                {
                    HighestTwo(Side1, out high1, out high2);
                    Console.WriteLine("{0} and {1} goes from side 1 to side 2, time taken = {2}", high1.Key, high2.Key, high1.Value);
                    Side1.Remove(high1.Key);
                    Side1.Remove(high2.Key);
                    Side2.Add(high1.Key, high1.Value);
                    Side2.Add(high2.Key, high2.Value);
                    totaltime += high1.Value;

                    low1 = LowestOne(Side2);
                    Console.WriteLine("{0} comes back to side 1, time taken = {1}", low1.Key, low1.Value);
                    Side2.Remove(low1.Key);
                    Side1.Add(low1.Key, low1.Value);
                    totaltime += low1.Value;
                    i++;
                }
            } while (Side1.Count > 2);

            KeyValuePair<string, int> low3, low4;
            LowestTwo(Side1, out low3, out low4);
            Console.WriteLine("{0} and {1} goes from side 1 to side 2, time taken = {2}", low3.Key, low4.Key, low4.Value);
            Side2.Add(low4.Key, low4.Value);
            Side2.Add(low3.Key, low3.Value);
            totaltime += low4.Value;

            Console.WriteLine("\n");
            Console.WriteLine("Total Time taken = {0}", totaltime);

        }

        public static void LowestTwo(Dictionary<string, int> a, out KeyValuePair<string, int> low1, out KeyValuePair<string, int> low2)
        {
            Dictionary<string, int> b = a;
            low1 = b.OrderBy(kvp => kvp.Value).First();
            b.Remove(low1.Key);
            low2 = b.OrderBy(kvp => kvp.Value).First();
        }

        public static void HighestTwo(Dictionary<string,int> a, out KeyValuePair<string,int> high1, out KeyValuePair<string,int> high2)
        {
            Dictionary<string, int> b = a;
            high1 = b.OrderByDescending(k => k.Value).First();
            b.Remove(high1.Key);
            high2 = b.OrderByDescending(k => k.Value).First();
        }

        public static KeyValuePair<string, int> LowestOne(Dictionary<string,int> a)
        {
            Dictionary<string, int> b = a;
            return b.OrderBy(k => k.Value).First();
        }
    }
}

Sample output for a random input provided which is 7 in this case, and 2 persons to cross at a time will be:

Enter number of persons
7
Enter the name and time taken by each
A
2
B
5
C
3
D
7
E
9
F
4
G
6
Shortest time and logic:
A and C goes from side 1 to side 2, time taken = 3
A comes back to side 1, time taken = 2
E and D goes from side 1 to side 2, time taken = 9
C comes back to side 1, time taken = 3
A and C goes from side 1 to side 2, time taken = 3
A comes back to side 1, time taken = 2
G and B goes from side 1 to side 2, time taken = 6
C comes back to side 1, time taken = 3
A and C goes from side 1 to side 2, time taken = 3
A comes back to side 1, time taken = 2
A and F goes from side 1 to side 2, time taken = 4


Total Time taken = 40
0

I mapped out the possible solutions algebraically and came out the with the fastest time . and assigning algebra with the list of A,B,C,D where A is the smallest and D is the biggest the formula for the shortest time is B+A+D+B+B or 3B+A+D or in wordy terms, the sum of second fastest times 3 and add with the Most Fastest and Most Slowest.

looking at the program there was also a question of increased items. Although I haven't gone through it, but I am guessing the formula still applies, just add till all items with the second item times 3 and sum of everything except 2nd slowest times. e.g. since 4 items are 3 x second + first and fourth. then 5 items are 3 x second + first, third and fifth. would like to check this out using the program.

also i just looked at the pdf shared above, so for more items it is the sum of 3 x second + fastest + sum of slowest of each subsequent pair.

looking at the steps for the optimized solution, the idea is -right - for two items going to the right the fastest is 1st and 2nd fastest , -left - then plus the fastest going back for a single item is the fastest item -right - bring the slowest 2 items, which will account for only the slowest item and disregard the second slowest. -left - the 2nd fastest item. -final right - the 1st and 2nd fastest again

so again summing up = 2nd fastest goes 3 times, fastest goes once, and slowest goes with 2nd slowest.

0

A simple algorithm is : assume 'N' is the number of people who can cross at same time and one person has to cross back bearing the torch

  1. When moving people from first side to second side preference should be given to the 'N' slowest walkers
  2. Always use fastest walker to take torch from second side to first side
  3. When moving people from first side to second side, take into consideration who will bring back the torch in the next step. If the speed of the torch bearer in next step will be equal to the fastest walker, among the 'N' slowest walkers, in the current step then instead of choosing 'N' slowest walker, as given in '1', choose 'N' fastest walkers

Here is a sample python script which does this: https://github.com/meowbowgrr/puzzles/blob/master/bridgentorch.py

0

A full scale solution... with all the console logs showing you each step in the brute force algo is here

The U2 Bridge Crossing Riddle

user3833732
  • 886
  • 9
  • 14