LeetCode刷题-190709-扩展:三水桶等分8升水的问题,妖怪和和尚过河问题

三水桶等分8升水的问题
参考书:《算法的乐趣》王晓华著
问题描述
有三个容积分别是 3 升、5 升和 8 升的水桶,其中容积为8升的水桶中装满水,容积为 3 升和容积为 5 升的水桶是空的。三个水桶都没有体积刻度,现在需要将大水桶中的8升水等分成两份,每份都是4升水,附加条件是只能使用另外两个空水桶,不能使用其他容器。
好像是小学的暑假作业中的题目,答案就不说了,主要分析如何用代码来解题。用人的思维方式来解题是如何通过倒水确定 1 升水,就能找到最终答案了。代码解题的基本思路是穷尽法,使用状态树模型来求解。
重点提示

  • 倒水动作是否可行的判断条件:不能向自身倒水;倒出水的桶不能为空;接收水的桶不能为空;
  • 使用深度遍历算法,判断当前路径遍历是否结束的条件:所有的可倒水的操作是否遍历结束;是否找到的最后的结果。

代码实现

class Chapter5_853
{
    const string full_state = "853";
    List listProcess = new List();

    public void Start()
    {
        listProcess.Add("800");
        SearchState();
    }

    private void SearchState()
    {
        string cur_state = listProcess[listProcess.Count-1];
        if (listProcess.Count>1 && cur_state == "440")
        {
            PrintResult();
            return;
        }
        for (int from = 0; from < 3; from++)
        {
            for (int to = 0; to < 3; to++)
            {
                if (to == from)
                {
                    //不能自己给自己倒水
                    continue;
                }
                SearchStateOnAction(cur_state, from, to);
            }
        }
    }
    private void SearchStateOnAction(string cur_state, int from, int to)
    {
        char[] next_state = new char[] { cur_state[0], cur_state[1], cur_state[2] };
        if (cur_state[from] == '0' || cur_state[to] == full_state[to]) return;
        int warter = (cur_state[from]-'0' < (full_state[to] - cur_state[to])) ? cur_state[from]-'0' : (full_state[to] - cur_state[to]);
        next_state[from] = (char)((int)cur_state[from] - warter);
        next_state[to] = (char)((int)cur_state[to] + warter);
        string next_str = string.Format("{0}{1}{2}", next_state[0], next_state[1], next_state[2]);
        if (!listProcess.Contains(next_str))
        {
            listProcess.Add(next_str);
            SearchState();
            listProcess.RemoveAt(listProcess.Count - 1);
        }
    }

    private void PrintResult()
    {
        for (int i = 0; i < listProcess.Count; i++)
        {

            if (i>0)
            {
                //输出操作方法
                int[] from = new int[] { Convert.ToInt32(listProcess[i - 1][0]), Convert.ToInt32(listProcess[i - 1][1]), Convert.ToInt32(listProcess[i - 1][2]) };
                int[] to = new int[] { Convert.ToInt32(listProcess[i][0]), Convert.ToInt32(listProcess[i][1]), Convert.ToInt32(listProcess[i][2]) };
                int from_index=-1, to_index=-1 , water = -1;
                for (int j = 0; j < 3; j++)
                {
                    if (from[j] > to[j])
                    {
                        from_index = j;
                        water = from[j] - to[j];
                    }
                    else if (from[j] < to[j])
                    {
                        to_index = j;
                        water = to[j] - from[j];
                    }
                }
                Console.WriteLine("{0}->{1}:{2}", from_index, to_index, water);
            }
            Console.WriteLine(listProcess[i]);
        }
    }
}

//调用代码
static void Main(string[] args)
{
    Chapter5_853 process = new Chapter5_853();
    process.Start();
}

整个代码根据书本上的介绍进行编写,并没有说先构造一个状态数,而是通过 深度优先的搜索算法,搜索结果和构建状态数是同步进行的。个人觉点比较重要的代码段:

if (!listProcess.Contains(next_str))
{
    listProcess.Add(next_str);
    SearchState();
    listProcess.RemoveAt(listProcess.Count - 1);
}

这段代码是深度优先搜索方法的关键点。
和这个算法类似的是第6章,妖怪和和尚过河问题

问题描述
有三个和尚和三个妖怪,有一条船能坐两个人,他们需要使用这条船过河。限制条件是,在两边的岸上和船上,妖怪的数量不能大于和尚的数量(妖怪会把和尚吃掉)。
重点提示
状态的数学模型中需要把船的位置也包含进去。船上面就两个人也就不存在妖怪数量大于和尚数量的情况,但是一定至少有一个人船才能到另一边。
个人实现代码

class Chapter6_323
    {
        private bool bLocal = true;
        int count = 0;
        private List list_states = new List();
        public void Start()
        {
            bLocal = true;
            //Console.WriteLine("+" + "33001");
            list_states.Add("33001");
            SearchState();
        }

        private void SearchState()
        {
            string cur_state = list_states[list_states.Count - 1];
            if (cur_state == "00330")
            {
                count++;
                PrintResult(count);
                return;
            }
            List list_act = new List();
            if (bLocal)
            {
                int local_monster = cur_state[0] - '0';
                int local_monk = cur_state[1] - '0';
                for (int i = 0; i <= local_monster; i++)
                {
                    for (int j = (2 - i)< local_monk?(2-i): local_monk; j>=0; j--)
                    {
                        if (i == 0 && j == 0) continue;
                        list_act.Add(string.Format("{0}{1}", i, j));
                    }
                }
            }
            else
            {
                int remote_monster = cur_state[2] - '0';
                int remote_monk = cur_state[3] - '0';
                for (int i = 0; i <= remote_monster; i++)
                {
                    for (int j = (2 - i) < remote_monk ? (2 - i) : remote_monk; j >= 0; j--)
                    {
                        if (i == 0 && j == 0) continue;
                        list_act.Add(string.Format("{0}{1}", i, j));
                    }
                }
            }
            foreach (string item_act in list_act)
            {
                string next_state = "";
                if (cur_state == "1320")
                {
                    int s = 1;
                }
                if (MackActionNewState(cur_state, item_act, out next_state))
                {
                    //Console.WriteLine("+" + next_state);
                    bLocal = !bLocal;
                    list_states.Add(next_state);
                    SearchState();
                    list_states.RemoveAt(list_states.Count - 1);
                    bLocal = !bLocal;
                    //Console.WriteLine("-" + next_state);
                }
            }
        }

        private bool MackActionNewState(string cur_state,string try_action,out string next_state)
        {
            next_state = cur_state;
            bool result = false;
            int local_monster = cur_state[0] - '0';
            int local_monk = cur_state[1] - '0';
            int remote_monster = cur_state[2] - '0';
            int remote_monk = cur_state[3] - '0';
            int move_monster = try_action[0]-'0';
            int move_monk = try_action[1] - '0';
            if (bLocal)
            {
                local_monster -= move_monster;
                local_monk -= move_monk;
                remote_monster += move_monster;
                remote_monk += move_monk;
            }
            else
            {
                local_monster += move_monster;
                local_monk += move_monk;
                remote_monster -= move_monster;
                remote_monk -= move_monk;
            }
            //直接检查是否有效
            //和尚是否会被妖怪吃掉
            if ((local_monk > 0 && local_monster > local_monk) || (remote_monk > 0 && remote_monster > remote_monk))
            {
                result = false;
            }
            else
            {
                result = true;
            }
            //是否回到之前的状态
            if (result)
            {
                //这里表示下一个状态,所以最后一位和bLocal是相反值,表示船的下一个状态的位置
                next_state = string.Format("{0}{1}{2}{3}{4}", local_monster, local_monk,remote_monster,remote_monk, (!bLocal)?'1':'0');
            }
            if (list_states.Contains(next_state))
            {
                result = false;
            }
            return result;
        }

        private void PrintResult(int count)
        {
            Console.WriteLine("方案{0}:",count);
            foreach (string item in list_states)
            {
                Console.WriteLine(item);
            }
        }

    }

把这段代码的注释打开就可以看到遍历时的过程:

 //Console.WriteLine("+" + next_state);
 bLocal = !bLocal;
  list_states.Add(next_state);
  SearchState();
  list_states.RemoveAt(list_states.Count - 1);
  bLocal = !bLocal;
  //Console.WriteLine("-" + next_state);

和书不太一样的地方是,我把次过河的情况先缩小了范围,然后再取判断限制条件和是否形成回路。

你可能感兴趣的:(刷算法题,LeetCode刷题)