算法也是很过瘾的~~用面向对象实现~夜过吊桥~算法
问题描述
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
定义一个方法,在某一边找过桥速度最慢的两个人
1 | List<Person> FindTwoFastSpeedPersons(List<Person> persons, Side side) |
定义一个方法,在某一边找过桥速度最快的两个人
1 | List<Person> FindTwoLowestSpeedPersons(List<Person> persons, Side side) |
定义一个方法,在某一边找过桥速度最慢的两个人
1 | Person FindFastSpeedPerson(List<Person> persons, Side side) |
定义一个方法,检测是否指定的person到达了指定的一边
1 | CheckPersonsPassedToTargetSide(List<Person> persons, Side targetSide) |
代码
Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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枚举类
1 2 3 4 5 6 7 8 | namespace PassRiver2._0 { public enum Side { Left = 0, Right = 1 } } |
IPassAction接口
1 2 3 4 5 6 7 | 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 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配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <? 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/
关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步