Bridge crossing puzzle

2020-05-15 04:05发布

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.

11条回答
神经病院院长
2楼-- · 2020-05-15 04:18

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

查看更多
倾城 Initia
3楼-- · 2020-05-15 04:20

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
-------------------------
查看更多
ゆ 、 Hurt°
4楼-- · 2020-05-15 04:22

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
查看更多
迷人小祖宗
5楼-- · 2020-05-15 04:23

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.

查看更多
一纸荒年 Trace。
6楼-- · 2020-05-15 04:23

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

查看更多
一纸荒年 Trace。
7楼-- · 2020-05-15 04:24

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}"
查看更多
登录 后发表回答