zoukankan      html  css  js  c++  java
  • 修道士和野人问题

      休闲时刻看看神经网络方面的书,发现了修道士和野人的问题,不禁勾引起我写算法的欲望,曾经的三只大老虎三只小老虎过河问题、人狼羊白菜过河问题、汉诺塔、哈夫曼等等各种算法瞬间在脑海中约隐约现,修道士和野人问题我以前好像没有解开,中午吃饭的时候在脑海中重新构造思路,下午耗了点时间把它干掉。(算法不在代码里,而在思想中;所以尽量不要看我的代码,而要仔细分析我写的思路) 

    题目:

      设有3个修道士和3个野人来到河边, 打算用一条船从河的左岸渡到河的右岸。但该船每次只能装载两个人, 在任何岸边野人的数目都不得超过修道士的人数, 否则修道士就会被野人吃掉。假设野人服从任何一种过河安排, 请问如何规划过河计划才能把所有人都安全地渡过河去。

    首先考虑总共有(3+1)*(3+1)= 16 种不同的状态(因为左岸可以有0,1,2,3个传教士,左岸可以有0,1,2,3个野人),所以可以考虑使用穷举法。

    使用如下C#程序语言:

        int MaxNum = 3;
        for (int monk = MaxNum; monk >= 0; monk--)
        {
            for (int savage = MaxNum; savage >= 0; savage--)
            {
             Console.Write("{{" + monk + "," + savage + "},{" + (MaxNum - monk) + "," + (MaxNum - savage) + "}} ");
            }
            Console.Write("
    ");
        }

    生成16种状态图↓↓↓↓↓↓↓↓↓↓↓

    状态图含义:

    {a,b}:a,左岸修道士数量;b,左岸野人数量。

    --------仅考虑左岸传教士和野蛮人数量(所有状态图)------------------------
    {3,3}   {3,2}   {3,1}   {3,0}
    {2,3}   {2,2}   {2,1}   {2,0}
    {1,3}   {1,2}   {1,1}   {1,0}
    {0,3}   {0,2}   {0,1}   {0,0}

    其中{3,3}是起始状态图;{0,0}是终止状态图。

     去除在任何岸边野人的人数超过修道士的人数,保留所有可满足的状态图:

    --------仅考虑左岸传教士和野蛮人数量(符合要求状态图)----------------------
    {3,3}   {3,2}   {3,1}   {3,0}
            {2,2}
                    {1,1}
    {0,3}   {0,2}   {0,1}   {0,0}

    可知,左岸修道士和野人的总数是先减少、再增加、再减少、再增加...直到左岸的总人数为零(这个由船行动的方向决定的,请仔细思考),所求的最终状态图 {0,0},而且每次增减人数最多为2个(条件提示:该船每次只能装载两个人),由此可知左岸总人数的增减趋势是: -1或-2、+1或+2、-1或-2、+1或+2...。

    左岸人头数作为参照:

    --------仅考虑左岸传教士和野蛮人数量(分组)------------------------
    heads num:6    {3,3}
    heads num:5    {3,2}
    heads num:4    {3,1} {2,2}
    heads num:3    {3,0} {0,3}
    heads num:2    {1,1} {0,2}
    heads num:1    {0,1}
    heads num:0    {0,0}

    将船的停靠岸考虑在内,其中L、R分别表示船在左岸或右岸:

    --------仅考虑左岸传教士和野蛮人数量,以及船的状态------------------------
    heads num:6 {3,3,L}
    heads num:5 {3,2,L} {3,2,R}
    heads num:4 {3,1,L} {3,1,R} {2,2,L} {2,2,R}
    heads num:3 {3,0,L} {3,0,R} {0,3,L} {0,3,R}
    heads num:2 {1,1,L} {1,1,R} {0,2,L} {0,2,R}
    heads num:1 {0,1,L} {0,1,R}
    heads num:0 {0,0,R}

    此时,初始状态图为 {3,3,L},终止状态图为{0,0,R}

    其实讲到这里,几乎答案已经很明显了。上面已经提到:左岸总人数的增减趋势是: -1或-2、+1或+2、-1或-2、+1或+2...,假设每种状态图只能使用一次,按照-1或-2、+1或+2的循环模式的增长趋势,可得到如下几种走法:

    --------可能的路径----------------------------------------
    {3,3,L} {3,1,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {0,2,L} {0,0,R}
    {3,3,L} {3,1,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {1,1,L} {0,0,R}
    {3,3,L} {2,2,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {0,2,L} {0,0,R}
    {3,3,L} {2,2,R} {3,2,L} {3,0,R} {3,1,L} {1,1,R} {2,2,L} {0,2,R} {0,3,L} {0,1,R} {1,1,L} {0,0,R}

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        public enum State
        {
            Left, Right
        }
    
        public class RiverBank
        {
            public State state = State.Left;
            public int monk = 0;
            public int savage = 0;
            public bool used = false;
        }
    
        public class Item
        {
            public int index = 0;
            public List<RiverBank> list = new List<RiverBank>();
        }
    
        class Program
        {
            const int MaxNum = 3;
            static State state = State.Left;
            public State GetState
            {
                get
                {
                    State r = state;
                    state = (state == State.Left ? State.Right : State.Left);
                    return r;
                }
            }
    
            static void Add(List<Item> list, int index, params RiverBank[] objs)
            {
                foreach (Item i in list)
                {
                    if (i.index == index)
                    {
                        foreach (RiverBank ii in objs)
                        {
                            bool flag = true;
                            for (int iii = 0; iii < i.list.Count; iii++)
                            {
                                if (i.list[iii].monk == ii.monk && i.list[iii].monk == ii.monk && i.list[iii].state == ii.state)
                                {
                                    flag = false;
                                    break;
                                }
                            }
    
                            if (flag)
                            {
                                i.list.Add(ii);
                            }
                        }
    
                        return;
                    }
                }
    
                Item item = new Item();
                item.index = index;
                item.list.AddRange(objs);
                list.Add(item);
            }
    
            static void WriteDivInfo(String msg)
            {
                Console.WriteLine("
    --------" + msg.PadRight(45, '-'));
            }
    
            static void WriteInfo(List<RiverBank> riverBankList)
            {
                foreach (var i in riverBankList)
                {
                    Console.Write("{" + i.monk + "," + i.savage + "," + (i.state == State.Right ? "R" : "L") + "} ");
                }
                Console.WriteLine();
            }
    
            static void Main(string[] args)
            {
                List<Item> list = new List<Item>();
    
                WriteDivInfo("仅考虑左岸传教士和野蛮人数量(所有状态图)");
                for (int monk = MaxNum; monk >= 0; monk--)
                {
                    for (int savage = MaxNum; savage >= 0; savage--)
                    {
                        Console.Write("{" + monk + "," + savage + "}	");
                    }
                    Console.Write("
    ");
                }
    
                WriteDivInfo("仅考虑左岸传教士和野蛮人数量(符合要求状态图)");
    
                for (int monk = MaxNum; monk >= 0; monk--)
                {
                    for (int savage = MaxNum; savage >= 0; savage--)
                    {
                        if (monk == 0 || monk == MaxNum || monk == savage)
                        {
                            int index = monk + savage;
                            //RiverBank p = new RiverBank { monk = monk, savage = savage };
                            //Add(list, index, p);
    
                            if (index > 0)
                            {
                                RiverBank p = new RiverBank { monk = monk, savage = savage, state = State.Left };
                                Add(list, index, p);
                            }
                            if (index < 2 * MaxNum)
                            {
                                RiverBank p = new RiverBank { monk = monk, savage = savage, state = State.Right };
                                Add(list, index, p);
                            }
                            Console.Write("{" + monk + "," + savage + "}	");
                        }
                        else
                        {
                            Console.Write("     	");
                        }
                    }
                    Console.Write("
    ");
                }
    
                WriteDivInfo("仅考虑左岸传教士和野蛮人数量,以及船的状态");
    
                foreach (Item item in list)
                {
                    Console.Write("heads num:" + item.index + "	");
                    foreach (RiverBank i in item.list)
                    {
                        //Console.Write("{" + i.monk + "," + i.savage + "} ");
                        Console.Write("{" + i.monk + "," + i.savage + "," + (i.state == State.Right ? "R" : "L") + "} ");
                    }
                    Console.Write("
    ");
                }
    
                list.Sort((a, b) => { return a.index < b.index ? 1 : -1; });
    
                WriteDivInfo("可能的路径");
    
                List<RiverBank> usedList = new List<RiverBank>();
                list[0].list[0].used = true;
                usedList.Add(list[0].list[0]);
                Cycle(ref list, ref usedList);
    
                Console.ReadKey();
            }
    
            private static void Cycle(ref List<Item> list, ref List<RiverBank> usedList)
            {
                RiverBank item = usedList[usedList.Count - 1];
                State state = item.state;
                int monk = item.monk;
                int savage = item.savage;
    
                for (int i = 1; i <= 2; i++)
                {//±1或±2
                    for (int j = 0; j <= i; j++)
                    {//每次变化的传教士数
                        int k = i - j;//每次变化的野人数
                        int value = (state == State.Left ? -1 : 1);
                        int m = monk + j * value;
                        int s = savage + k * value;
    
                        if (m == 0 && s == 0 && state == State.Left)
                        {
                            list[6].list[0].used = true;
                            usedList.Add(list[6].list[0]);
                            WriteInfo(usedList);
                            list[6].list[0].used = false;
                            usedList.Remove(list[6].list[0]);
                        }
                        else if (m >= 0 && s >= 0 && m <= MaxNum && s <= MaxNum)
                        {
                            foreach (var it in list)
                            {
                                foreach (var v in it.list)
                                {
                                    if (v.used == false && v.state != state && v.monk == m && v.savage == s)
                                    {
                                        v.used = true;
                                        usedList.Add(v);
                                        Cycle(ref list, ref usedList);
                                        v.used = false;
                                        usedList.Remove(v);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    源代码
  • 相关阅读:
    BeagleBone Black安装小米随身WiFi驱动方法
    java-ee,ssh整合博文收藏
    BeagleBoneBlack Linux开发相关链接收藏
    Debian7.7 wheezy 中源码安装emacs24
    GLOG使用注意事项
    centos、linux改变ll命令显示颜色
    centos中samba配置后始终连不上的绝招
    中兴电信光纤猫F612管理员密码获取方法
    STM32W108芯片的SWD在IAR7.30版本中不能用
    TRF7960天线参数试验
  • 原文地址:https://www.cnblogs.com/preacher/p/5997442.html
Copyright © 2011-2022 走看看