問題描述
1.五個人打算過一座吊橋,開始時他們都位於該橋的一側。
2.天很黑,五個人手里只有一個手電筒。
3.該橋一次最多只能同時過兩個人,無論是一個人還是兩個人過橋,都需要攜帶手電筒看路。而且手電筒只能通過人攜帶過橋的方式傳遞。
4.第一個人過橋需要1分鍾時間,第二個人過橋需要2分鍾,第三個人需要5分鍾,第四個需要7分鍾,第五個需要10分鍾。由於速度不同,兩個人一起過橋的話,速度以慢的人為准。
問題:求最快過橋時間。要求寫出求解的算法。

分析題目
1.從左邊到右邊,需要有一個人拿着手電筒,到達右邊后,需要有一個人折返回到左邊,那么怎么才能最大化的減少返回時間?
答:那么這個送回手電筒的人一定是右邊最快的。
2.兩人同時過橋,取最大時間,那怎么才能保證最大化的利用最長時間呢?
答:在左邊選最長時間的兩個人一起過橋。
3.怎么保證右邊折返回來回左邊的人所花的時間最短?
答:左邊選擇最快的兩個人一起到右邊,然后最快的折返回左邊,次快的等待最慢的兩個人過來后,再折返回左邊。重復這個步驟即可保證折返回左邊的人所花的時間最短。
我們來試着算一下,最短需要多長時間。
(1)選擇左邊最快的兩個人先過去,1分鍾和2分鍾的先過去,總共花費2分鍾。現在左邊5,7,10。右邊1,2。
(2)選擇右邊最快的一個人折返回來,1分鍾的回來,總共花費2分鍾+1分鍾=3分鍾。現在左邊5,7,10。右邊2。
(3)選擇左邊最慢的兩個人過去,7分鍾的和10分鍾的過去,總共花費3分鍾+10分鍾=13分鍾。現在左邊5,1。右邊2,7,10。
(4)選擇右邊最快的一個人折返回來,2分鍾的回來,總共花費13分鍾+2分鍾=15分鍾。現在左邊5,1,2。右邊7,10。
(5)選擇左邊最快的兩個人先過去,1分鍾和2分鍾的先過去,總共花費15分鍾+2分鍾。現在左邊5。右邊7,10,1,2。
(6)選擇右邊最快的一個人折返回來,1分鍾的回來,總共花費17分鍾+1分鍾=18分鍾。現在左邊5,1。右邊7,10,2。
(5)選擇左邊最慢的兩個人過去,5分鍾的1分鍾的過去,總共花費18分鍾+5分鍾=23分鍾。現在左邊沒有。右邊7,10,2,5,1。
總共花費23分鍾。
總結一下上面的規律:
最快的兩個人過去,最快一個人回來,最慢的兩個人過去,最快的一個人回來。循環這個步驟。
怎么實現上面的算法?
這里我們可以用面向對象的方法。
定義一個Person類。
包含的屬性:
(1)過橋速度Speed。(1分鍾/2分鍾/5分鍾/7分鍾/10分鍾)
(2)當前在橋的哪一邊Side(左邊/右邊)
包含的方法:從左邊走到右邊的方法,或者從右邊折返回左邊的方法,命名為Pass(Side side);
定義一個接口IPassAction,包含一個方法void Pass(Side side);
定義一個枚舉類型Side,包含Left、Right

定義一個方法,在某一邊找過橋速度最慢的兩個人
List<Person> FindTwoFastSpeedPersons(List<Person> persons, Side side)
定義一個方法,在某一邊找過橋速度最快的兩個人
List<Person> FindTwoLowestSpeedPersons(List<Person> persons, Side side)
定義一個方法,在某一邊找過橋速度最慢的兩個人
Person FindFastSpeedPerson(List<Person> persons, Side side)
定義一個方法,檢測是否指定的person到達了指定的一邊
CheckPersonsPassedToTargetSide(List<Person> persons, Side targetSide)
代碼
Person類
namespace PassRiver2._0
{
public class Person : IPassAction
{
private int _speed;
private Side _side;
public Person(int speed, Side side)
{
_speed = speed;
_side = side;
}
public int Speed
{
get { return _speed; }
set { _speed = value; }
}
public Side Side
{
get { return _side; }
set { _side = value; }
}
public void Pass(Side side)
{
_side = side;
}
}
}
Side枚舉類
namespace PassRiver2._0
{
public enum Side
{
Left = 0,
Right = 1
}
}
IPassAction接口
namespace PassRiver2._0
{
public interface IPassAction
{
void Pass(Side side);
}
}
公共方法
List<Person> FindTwoFastSpeedPersons(List<Person> persons, Side side)
List<Person> FindTwoLowestSpeedPersons(List<Person> persons, Side side)
Person FindFastSpeedPerson(List<Person> persons, Side side)
CheckPersonsPassedToTargetSide(List<Person> persons, Side targetSide)
private static List<Person> FindTwoLowestSpeedPersons(List<Person> persons, Side side)
{
List<Person> sortedPersons = persons.Where(s=> s.Side == side).OrderByDescending(s => s.Speed).ToList();
List<Person> passedPersons = new List<Person>();
if (persons.Count > 1)
{
passedPersons = new List<Person>();
passedPersons.Add(sortedPersons[0]);
passedPersons.Add(sortedPersons[1]);
}
else if (persons.Count == 1)
{
passedPersons.Add(sortedPersons[0]);
}
else
{
}
return passedPersons;
}
private static List<Person> FindTwoFastSpeedPersons(List<Person> persons, Side side)
{
List<Person> sortedPersons = persons.Where(s=> s.Side == side).OrderBy(s => s.Speed).ToList();
List<Person> passedPersons = new List<Person>();
if (persons.Count > 1)
{
passedPersons = new List<Person>();
passedPersons.Add(sortedPersons[0]);
passedPersons.Add(sortedPersons[1]);
}
else if (persons.Count == 1)
{
passedPersons.Add(sortedPersons[0]);
}
else
{
return null;
}
return passedPersons;
}
private static Person FindFastSpeedPerson(List<Person> persons, Side side)
{
if (persons.Count > 0)
{
List<Person> sortedPersons = persons.Where(s => s.Side == side).OrderBy(s => s.Speed).ToList();
return sortedPersons[0];
}
else
{
return null;
}
}
private static bool CheckPersonsPassedToTargetSide(List<Person> persons, Side targetSide)
{
bool isPassed = false;
foreach (Person person in persons)
{
if (person.Side != targetSide)
{
isPassed = false;
return isPassed;
}
}
isPassed = true;
return isPassed;
}
Main方法
static void Main(string[] args)
{
Type type = MethodBase.GetCurrentMethod().DeclaringType;
//創建日志記錄組件實例
ILog log = log4net.LogManager.GetLogger(type);
//記錄錯誤日志
log.Info("Start");
List<Person> persons = new List<Person>();
persons.Add(new Person(1, Side.Left));
persons.Add(new Person(2, Side.Left));
persons.Add(new Person(5, Side.Left));
persons.Add(new Person(7, Side.Left));
persons.Add(new Person(10, Side.Left));
int totalTime = 0;
while (!CheckPersonsPassedToTargetSide(persons, Side.Right))
{
List<Person> twoFastSpeedPersons = FindTwoFastSpeedPersons(persons, Side.Left);
foreach (Person person in twoFastSpeedPersons)
{
person.Pass(Side.Right);
}
if (twoFastSpeedPersons.Count > 1)
{
totalTime += twoFastSpeedPersons[1].Speed;
}
else if (twoFastSpeedPersons.Count == 1)
{
totalTime += twoFastSpeedPersons[0].Speed;
}
else
{
}
//-------------
log.Info("---Go To Right---------->");
foreach (Person person in twoFastSpeedPersons)
{
log.Info(person.Speed);
}
log.Info("Total Time:" + totalTime);
//-------------
if (CheckPersonsPassedToTargetSide(persons, Side.Right))
{
break;
}
Person fastSpeedPerson = FindFastSpeedPerson(persons, Side.Right);
fastSpeedPerson.Pass(Side.Left);
totalTime += fastSpeedPerson.Speed;
//-------------
log.Info("<----------Go Back Left---");
log.Info(fastSpeedPerson.Speed);
log.Info("Total Time:" + totalTime);
//-------------
if (CheckPersonsPassedToTargetSide(persons, Side.Right))
{
break;
}
List<Person> twoLowestSpeedPersons = FindTwoLowestSpeedPersons(persons, Side.Left);
foreach (Person person in twoLowestSpeedPersons)
{
person.Pass(Side.Right);
}
totalTime += twoLowestSpeedPersons[0].Speed;
//-------------
log.Info("---Go To Right---------->");
foreach (Person person in twoLowestSpeedPersons)
{
log.Info(person.Speed);
}
log.Info("Total Time:" + totalTime);
//-------------
if (CheckPersonsPassedToTargetSide(persons, Side.Right))
{
break;
}
fastSpeedPerson = FindFastSpeedPerson(persons, Side.Right);
fastSpeedPerson.Pass(Side.Left);
totalTime += fastSpeedPerson.Speed;
//-------------
log.Info("<----------Go Back Left---");
log.Info(fastSpeedPerson.Speed);
log.Info("totalTime:" + totalTime);
//-------------
if (CheckPersonsPassedToTargetSide(persons, Side.Right))
{
break;
}
}
log.Info("------------Total Time-------------");
log.Info(totalTime);
Console.ReadKey();
}
Log4Net配置:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
</configSections>
<!--站點日志配置部分-->
<log4net>
<root>
<!--控制級別,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
<!--比如定義級別為INFO,則INFO級別向下的級別,比如DEBUG日志將不會被記錄-->
<!--如果沒有定義LEVEL的值,則缺省為DEBUG-->
<level value="DEBUG"/>
<appender-ref ref="PassRiverLogger2"/>
</root>
<appender name="PassRiverLogger2" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>
</configuration>
結果:

注意:
這種算法只適合部分情況。比如5個人的過橋速度是1分鍾、10分鍾、11分鍾、12分鍾、13分鍾,則用這種算法算出來的56分鍾。但是如果1分鍾和其他四個人分別過橋,每次都是1分鍾的回來,則總時間是10+11+12+13+1*3=49(分鍾)。
感謝Debuggings同學的指正!
本文首鏈:算法也是很過癮的~~夜過吊橋算法 http://www.cnblogs.com/jackson0714/p/algorithm_1.html
作 者: Jackson0714
出 處:http://www.cnblogs.com/jackson0714/
關於作者:專注於微軟平台的項目開發。如有問題或建議,請多多賜教!
版權聲明:本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。
特此聲明:所有評論和私信都會在第一時間回復。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信我
聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!
