修道士和野人問題


  休閑時刻看看神經網絡方面的書,發現了修道士和野人的問題,不禁勾引起我寫算法的欲望,曾經的三只大老虎三只小老虎過河問題、人狼羊白菜過河問題、漢諾塔、哈夫曼等等各種算法瞬間在腦海中約隱約現,修道士和野人問題我以前好像沒有解開,中午吃飯的時候在腦海中重新構造思路,下午耗了點時間把它干掉。(算法不在代碼里,而在思想中;所以盡量不要看我的代碼,而要仔細分析我寫的思路) 

 

題目:

  設有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 = (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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
源代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM