首页 > 代码库 > 修道士和野人问题

修道士和野人问题

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

 

题目:

  设有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("\n");
    }

生成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("\n--------" + 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 + "}\t");
                }
                Console.Write("\n");
            }

            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 + "}\t");
                    }
                    else
                    {
                        Console.Write("     \t");
                    }
                }
                Console.Write("\n");
            }

            WriteDivInfo("仅考虑左岸传教士和野蛮人数量,以及船的状态");

            foreach (Item item in list)
            {
                Console.Write("heads num:" + item.index + "\t");
                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("\n");
            }

            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 = http://www.mamicode.com/(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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
源代码

 

修道士和野人问题