Crossing Bridge Problem

Problem Statement
   
A well-known riddle goes like this: Four people are crossing an old bridge. The bridge cannot hold more than two people at once. It is dark, so they can't walk without a flashlight, and they only have one flashlight! Furthermore, the time needed to cross the bridge varies among the people in the group. For instance, let's say that the people take 1, 2, 5 and 10 minutes to cross the bridge. When people walk together, they always walk at the speed of the slowest person. It is impossible to toss the flashlight across the bridge, so one person always has to go back with the flashlight to the others. What is the minimum amount of time needed to get all the people across the bridge?




In this instance, the answer is 17. Person number 1 and 2 cross the bridge together, spending 2 minutes. Then person 1 goes back with the flashlight, spending an additional one minute. Then person 3 and 4 cross the bridge together, spending 10 minutes. Person 2 goes back with the flashlight (2 min), and person 1 and 2 cross the bridge together (2 min). This yields a total of 2+1+10+2+2 = 17 minutes spent.


You want to create a computer program to help you solve new instances of this problem. Given an int[] times, where the elements represent the time each person spends on a crossing, your program should return the minimum possible amount of time spent crossing the bridge.

 
Definition
Class: BridgeCrossing
Method: minTime
Parameters: int[]
Returns: int
Method signature: int minTime(int[] times)
(be sure your method is public)
    
 
Notes
- In an optimal solution, exactly two people will be sent across the bridge with the flashlight each time (if possible), and exactly one person will be sent back with the flashlight each time. In other words, in an optimal solution, you will never send more than one person back from the far side at a time, and you will never send less than two people across to the far side each time (when possible).
 
Constraints
- times will have between 1 and 6 elements, inclusive.
- Each element of times will be between 1 and 100, inclusive.
 
Examples
0)
   
{ 1, 2, 5, 10 }
Returns: 17
The example from the text.
1)
   
{ 1, 2, 3, 4, 5 }
Returns: 16
One solution is: 1 and 2 cross together (2min), 1 goes back (1min), 4 and 5 cross together (5min), 2 goes back (2min), 1 and 3 cross together (3min), 1 goes back (1min), 1 and 2 cross together (2min). This yields a total of 2 + 1 + 5 + 2 + 3 + 1 + 2 = 16 minutes spent.
2)
   
{ 100 }
Returns: 100
Only one person crosses the bridge once.
3)
   
{ 1, 2, 3, 50, 99, 100 }
Returns: 162

This problem statement is the exclusive and proprietary property of TopCoder, Inc. Any unauthorized use or reproduction of this information without the prior written consent of TopCoder, Inc. is strictly prohibited. (c)2010, TopCoder, Inc. All rights reserved.


This TopCoder problem isinteresting, and, actually surprisingly simple if using brute force algorithm.  Here are some points about this problem:
1) if there are only one or two people, the solution is very straight forward,  the maximum number of the 2
2) if there are more than 2 people, then each turn has 2 parts: 1: 2 people come across the bridge, 2:1 people come back., so each turn can make one people come across the bridge, except for the last turn, 2 people come across the bridge and no one come back.
3) from above description, it is very clear that we can use recursive algorithm to solve this problem.
4) This problem's complexity is daunting, if there are N people(N>=3), the combination of 2 people is N(N-1)/2, then one people come back, next turn is (N-1)*(N-2)/2, down to n=3 which is 3*2/2 = 3. Suppose we have 3 people A, B, and C, the combinations are AB, AC, and BC. the sequence starting from 3 is: 3, 6, 10... An+1 = An+n.
for a bigger N, this combinational complexity is roughly O(n^2n), plus the O(n) complexity for each combination, total complexity is roughly O(n*n^2n).  Yet, according to the problem statement, total people is no more than 6, so we can use brute force algorithm to solve this problem, and, it seems the only way to find the optimal solution.

5) My solution can not only find the optimal time, but also can print one optimal schedule.
6) I can see that this problem can has some extensions, such as bridge can allow more than 2 people, each person can have a weight and the bridge has a weight limit, or, allow people to travel as fast as possible, etc. 

Below is the C++ code, I used an int crossing_times[] array to represent peoples, and used a vector<ing> remaining constructed from the array to represent the people remaining at this side of the bridge, and another vector<int> crossed to represent those who have come across the bridge. Actually I could use one vector<int> to hold all people, change the minutes of  those came across the bridge's to negative as a sign. But this will not do us any many benefits, because, although you do not need to push_back and erase(this is O(n)) data from vectors, but you will need to find out which is remaining and which is at the other side, this is also an O(n) operation.
Another consideration to improve performance is to multithread it , but I will not write an multithreaded version here.


The sample output for 6 people with respectively {1, 2, 3, 50, 99, 100} minutes of crossing bridge time is like this:

Best time is: 162
The schedule is: (1, 2)->  (1)<-  (3, 50)->  (2)<-  (1, 2)->  (1)<-  (99, 100)->  (2)<-  (1, 2)->

first (1, 2) come across the bridge and (1) comes back, this round takes 3 minutes

then (3, 50) come across the bridge and (2) comes back, this round takes 52 minutes

then (1, 2) come across the bridge and (1) comes back, this round takes 3 minutes 

 then (99, 100) come across the bridge and (2) comes back, this round takes 102 minutes

finally (1, 2) come across the bridge, this round takes 2 minutes.

So, altogether it takes 162 minutes for these 6 people to come across the bridge.


if you re-arrange the 6 people in this order {100, 2, 99, 3, 50, 1}, the output will be like:

Best time is: 162
The schedule is: (2, 1)->  (1)<-  (100, 99)->  (2)<-  (3, 1)->  (1)<-  (50, 1)->  (1)<-  (2, 1)->

the Best time is just the same.


//
// Copyright DGU, gdyxgzy@hotmail.com
// You can use part or whole of this code freely, provided that you keep this headert, and
// provide a link to this page or send an email to my inbox listed above.
//

#include <iostream>
#include <vector>
#include <string>
#include <limits>
#include <algorithm>
#include <chrono>

using namespace std;

int Cross(vector<int>& remaining, vector<int>& crossed, int idx1, int idx2, string& schedule)
{
	int x1 = remaining[idx1];
	int x2 = remaining[idx2];
	crossed.push_back(x1);
	crossed.push_back(x2);
	remaining.erase(remaining.begin() + idx1);
	remaining.erase(std::find(remaining.begin(), remaining.end(), x2));
	schedule += "(" + std::to_string(x1) + ", " + std::to_string(x2) + ")->  ";

	vector<int>::iterator itr = std::min_element(crossed.begin(), crossed.end());
	int t = *itr;
	remaining.push_back(t);
	schedule += "(" + std::to_string(t) + ")<-  ";
	crossed.erase(itr);
	return t;
}

static int best_time = std::numeric_limits<int>::max();
static string best_schedule;

int BridgeCrossing(const vector<int>& remaining, const vector<int>& crossed, int cur_time, string& schedule)
{
	if (remaining.size() == 2)
	{
		int tm = cur_time + std::max(remaining[0], remaining[1]);
		if (tm < best_time)
		{
			best_time = tm;
			best_schedule = schedule + "(" + std::to_string(remaining[0]) + ", " + std::to_string(remaining[1]) + ")->  ";
			return tm;
		}
		else return -1;
	}

	for (unsigned int i = 0; i < remaining.size() - 1; ++i)
	{
		for (unsigned int j = i + 1; j < remaining.size(); ++j)
		{
			int tm = cur_time + std::max(remaining[i], remaining[j]);
			if (tm >= best_time)
				continue;
			vector<int> cur_remain = remaining;
			vector<int> cur_crossed = crossed;
			string cur_sch = schedule;

			tm += Cross(cur_remain, cur_crossed, i, j, cur_sch);
			if (tm >= best_time)
				continue;

			int t = BridgeCrossing(cur_remain, cur_crossed, tm, cur_sch);
			if (t < 0)
				continue;
			else
				tm += t;

			if (tm < best_time )
			{
				best_time = tm;
				best_schedule = cur_sch;
			}
			continue;
		}
	}
	return best_time;
}


int main(void)
{
	//int crossing_times[] = { 1, 2, 5, 10 };
	//int crossing_times[] = { 1, 2, 3, 4, 5 };
	int crossing_times[] = { 1, 2, 3, 50, 99, 100 };
	//int crossing_times[] = { 2, 2, 3, 5, 5, 6, 7, 8, 10, 12 };

	vector<int> this_side(std::begin(crossing_times), std::end(crossing_times));
	vector<int> other_side;

	auto t0 = chrono::high_resolution_clock::now();
	BridgeCrossing(this_side, other_side, 0, string());
	auto t1 = chrono::high_resolution_clock::now();

	cout << "Best time is: " << best_time << endl;
	cout << "The schedule is: " << best_schedule << endl;
	cout << "Computational time is: " << chrono::duration_cast<chrono::milliseconds>(t1 - t0).count() / 1000.0 << " seconds" << endl;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值