怪物过河问题

高一的时候,同学(老友)问过这个题,我现在还记得他当时的话语:“这道题,我们那边的一个大学生想了三天都没想出来,呵呵,让我解决了,厉害吧,哈哈。。。”。当时,我也是冲着他那句“大学生三天都没想出来”,才兴致勃勃地答应挑战的,可惜,当时终究是没想出来,最后,只好逼着他讲答案。当然,经历过大学的人都知道,其实大学生也就那样,大多很普通,无须过高的评价。

题意大致如下:

有6个怪物AaBbCc,ABC是大怪物,abc是小怪物,Aa、Bb、Cc是亲子关系,当小怪物的父亲不在身边时,别的大怪物会吃这个小怪物,大怪物不会吃自己的孩子,现在这6个怪物要过河,这边有一条船,船最多可载2只大怪物,或3只小怪物,当然也可以载1只大怪物和1只小怪物,其中知道ABCa会划船,bc不会,想办法让6个怪物都安全到对岸。

 

大学后,我开始尝试用编程来解决它,但每每不能成功,最大的难点是,我不知如何把实际问题抽象出来。这样,每过一年,我都会再尝试一次。

 

“辛波那契数列”也是我大学时遇到的一个难题,不过,在去年的时候已经解决了,但在我的博客中居然没有相关信息,所以上篇文章再写了这个问题。

 

想到“怪物过河问题”是我之前想解决而又未解决的最后一个问题,昨天晚上又开始了新的尝试,还好这次编码非常顺利,终于,抽象出问题来了,中间的一些小的编码漏洞,和个别特殊情况引发的思维漏洞,让我找了一个通宵,早上睡了4个小时,出去购物回来已是中午2点,继续找,终于全部把漏洞全补好了。

我的实现如下:

#include <list>
#include <string>
#include <iostream>
using namespace std;

struct Monster
{
    char        place;
    const char  name;
    const bool  paddle;
    const short weight;
};

Monster monster[6] =
{
    { 'H', 'A', true,  3 },
    { 'H', 'B', true,  3 },
    { 'H', 'C', true,  3 },
    { 'H', 'a', true,  2 },
    { 'H', 'b', false, 2 },
    { 'H', 'c', false, 2 }
};

int left_count = 6; /* 剩余需要过河的怪物 */
const int boat_load_weight = 6; /* 船的最大载重量 */
list<int> move_index_list; /* 记录每次过河的怪物 */

static bool check(int, char);
static bool move(char, char);
static bool try_move(int, char, char);
static void change_place(int, char);
static void display_way();
void display_state(bool go);

/*
 * 检测此次过河方案是否安全
 */
bool check(int current_move_index, char to)
{
    /*
     * 不允许出现相同的怪物做往返过河的动作
     * 比如: Xx(H->T), Xx(H<-T)
     * 下面特例也需视为此情形:
     * 1. Yy(->), Zz(<-))
     * 2. Zz(->), Yy(<-))
     */
    if (!move_index_list.empty())
    {
        int last_move_index = move_index_list.back();
        if (last_move_index == current_move_index ||
            (25 == last_move_index &&
             36 == current_move_index) ||
            (36 == last_move_index &&
             25 == current_move_index))
        {
            return(false);
        }
    }

    /* 检查各小怪物 (xyz) 是否安全 */
    for (int i = 3; i < 6; ++i)
    {
        const Monster & young = monster[i];
        const Monster & old = monster[i - 3];
        if (young.place == old.place)
        {
            continue; /* 此时 young 必定安全 */
        }

        for (int j = 0; j < 3; ++j)
        {
            if (i - 3 == j)
            {
                continue;
            }

            /*
             * 1.检查船在河中时, young 是否有安全
             * 2.检查船到彼岸后, young 是否有安全
             */
            const Monster & enemy = monster[j];
            if (young.place == enemy.place ||
                (to == young.place &&
                 'B' == enemy.place &&
                 'B' != old.place) ||
                ('B' == young.place &&
                 to == enemy.place &&
                 to != old.place))
            {
                return(false);
            }
        }
    }

    if ('T' == to) /* 'T' 代表 There */
    {
        return(true);
    }

    /*
     * (否则)如果船是返回方向的, 此时
     * 不能变成最初的状态, 即:
     * 不能出现所有怪物都回来的现象
     * 比如:
     * xyz(H->T), xy(H<-T), x(H->T), xy(H<-T)
     * 且不论上面四步是在做无用功,
     * 最可怕的是会使搜索陷入无限递归中
     */
    for (int j = 0; j < 6; ++j)
    {
        co
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值