[c++]阿拉伯夫妇过河

         三对阿拉伯夫妇要过河,一只小船只能载二人。丈夫不在场的情况下,妻子身边不能有其他男人。如何过河?

算法

         简单明了,回溯法。

数据结构

         先定义数据结构。

 //三对夫妇,一共六人
#define COUPLE_COUNT  3
struct riverside
{
         bool _has_boat;
         char _person[COUPLE_COUNT * 2];
};
 
riverside left, right;

第一个布尔变量表示船是否在岸边,如果岸边没有船,那么不准过河。

第二个数组是一个位图,对应下标如果是1,表示当前此人在河岸这边,为0,则不在这边。

一开始,所有夫妇在左岸,这个数组全为一。下标为偶数的元素是丈夫,下标为奇数的下标是妻子。

如果从左岸过去第一个妻子和第二个妻子,那么数组变为以下状态:

[c++]阿拉伯夫妇过河

与此同时,右岸的数组变为:

过河策略

         每个人有在和不在两种状态,一共有六人,那么要从左岸挑选过河的人选,最多有26次方也就是64种方式。如果可以过去的话,船运行的次数不会超过64

063,每个数字代表一个过河策略。我们只要把这个数字转换成二进制形式,就可以得到一个过河方法。比如,数字5,转换成二进制就是000101,为了编程方便,我们将其倒置,也就是101000,这就表示要让第一个丈夫和第二个丈夫过河。

[c++]阿拉伯夫妇过河

我们要遍历所有方法,只要从1便利到63即可,0表示一个人也不运,不用考虑。当然,这里肯定有很多违规的过河方法,我们要把它们丢掉。

 

主函数

伪代码如下:

void cross_river(riverside from, riverside to)
{
    for (int i = 0; I < 63; i++)
    {
        if (!can_cross(I, from, to))
            continue;
        else
        {
             Cross(); //开船,走一趟
             If (Succeed())
             {
                  Printf(“ok!!”);
                  Exit(0);  //如果过河成功,直接退出,简单粗暴
             }
             Cross_river(to, from); //递归调用,注意,交换一下变量。从左到右,然后从右到左。
         }
     }
}

can_cross函数,是我们重点要实现的函数。

他由以下几个部分组成。

先写判断河岸边众人能否共处的函数。

 bool riverside::sharia_judge()
{
     for (int i = 0; i < COUPLE_COUNT; i++)
     {
         if (_person[i * 2 + 1] != 0) // 如果河岸有妻子
         {
              if (_person[i * 2] != 0) // 如果她丈夫也在,ok
              {
                   continue;
              }
              else //如果她丈夫不在
              {
                   for (int j = 0; j < COUPLE_COUNT; j++) //遍历所有的丈夫
                       if (_person[j * 2] != 0) // 如果有其他丈夫。走到这个分之,就不会有她丈夫了
                       {
                            return false;
                       }
              }
          }
      }
      return true;
}

不走回头路

          为了避免刚把一个人运过去,又把这人运回来,进而无限循环下去的场景。我们在过河前,需要记住当前河岸边都有哪些人,船是否在,下次过河的时候要判断一下,如果状态又回到之前了,那么这种过河方式就不允许。

 

过河策略的选择

         过河策略是一个数字。代码不贴了,需要判断以下几点:

(1)       岸边没有船,不能过。

(2)       要运的人不在,这个策略丢弃。

(3)       要运的人超过两个,这个策略丢弃。不能超载。

(4)       运送的人是一男一女,如果不是夫妻,这个策略丢弃。

(5)  如果过河后,又回到之前的状态了,这一步不能走。

 

具体参见代码。

直接贴结果。

[c++]阿拉伯夫妇过河

 #include <stdio.h>
#include <set>
#include <stdlib.h>
#include <vector>
using namespace std;
#define COUPLE_COUNT  3

struct riverside
{
 bool _has_boat; // 这个变量似乎没用
 char _person[COUPLE_COUNT * 2];
 riverside()
 {
  _has_boat = true;
  memset(_person, 1, COUPLE_COUNT * 2);
 }
 int count()
 {
  int i = 0;
  int ret = 0;
  for (; i < COUPLE_COUNT * 2; i++)
  {
   if (_person[i] == 1)
   {
    ret++;
   }
  }
  return ret;
 }
 void print() const
 {
  char *person = "AaBbCcDdEe";
  for (int i = 0; i < COUPLE_COUNT * 2; i++)
  {
   if (_person[i] != 0)
   {
    if (i % 2 == 0)
     printf("%c", person[i]);
    else
     printf("%c", person[i]);
   }
   else
   {
    printf(".");
   }
  }
 }
 bool sharia_judge()
 {
  int shangren = 0;
  int yeren = 0;
  for (int i = 0; i < COUPLE_COUNT; i++)
  {
   if (_person[i * 2] != 0)
   {
    shangren++;
   }
   if (_person[i * 2 + 1])
   {
    yeren++;
   }
  }
  return shangren >= yeren;
 }
};
// 操作符要重载一下,不然没法用 set<> 
bool operator < (const riverside& left, const riverside& right)
{
 if (left._has_boat == false && right._has_boat == true)
 {
  return true;
 }
 for (int i = 0; i < COUPLE_COUNT * 2; i++)
 {
  if (left._person[i] < right._person[i])
  {
   return true;
  }
  if (left._person[i] > right._person[i])
  {
   return false;
  }
 }
 return false;
}

bool operator == (const riverside& left, const riverside& right) // 这个函数没用到
{
 return memcmp(&left, &right, sizeof(left)) == 0;  
}
set<riverside> g_history_left;  // 左岸所有的状态,不能走回头路
set<riverside> g_history_right; // 右岸所有的状态,不能走回头路
// 一条过河记录
struct record
{
 int _no;
 riverside _from;
 riverside _to;
};
vector<record> g_records; // 这个堆栈记录过河记录
void print(const riverside &from, const riverside& to, int no)
{
 printf("[%02d]  ", no);
 const riverside* left, *right;
 if (no % 2 == 1)
 {
  left = &from;
  right = &to;
 }
 else
 {
  left = &to;
  right = &from;
 }
 
 left->print();
 if (left->_has_boat)
 {
  printf("|~<||>~");
 }
 else
 {
  printf("|~~~~~~");
 }
 printf("~~~~~~~~~~~~~");
 if (right->_has_boat)
 {
  printf("~<||>~|");
 }
 else
 {
  printf("~~~~~~|");
 }
 right->print();
 printf("\n");
}
void print_records()  // 过河完毕,把过河记录打印出来
{
 int i = 0;
 for (; i < g_records.size(); i++)
 {
  const record& rcd = g_records[i];
  print(rcd._from, rcd._to, rcd._no);
 }
}
int no = 0; // 第几趟过河
void covert(int index, int arry[]) // 把一个整数转换成二进制表达式,不过是倒过来的
{
 for (int i = 0; i < COUPLE_COUNT * 2; i++)
 {
  arry[i] = index % 2;
  index /= 2;
 }
}
bool can_cross(int first, int second, riverside from, riverside to)
{
 if (!from._has_boat)
 {
  return false;
 }
 if (first >= 0)
 {
  from._person[first] = 0;
  to._person[first] = 1;
 }
 if (second >= 0)
 {
  from._person[second] = 0;
  to._person[second] = 1;
 }
 from._has_boat = false;
 to._has_boat = true;
 
 if (!from.sharia_judge() || !to.sharia_judge())
 {
  return false;
 }
 
 if (no % 2 == 0) // 从右到左渡河,判断这次过河后,是不是又走回老路了.
 {
  if (g_history_left.find(to) != g_history_left.end())
  {
   return false;
  }
 }
 else // 从左到右渡河,判断过河后,是不是又走回老路了
 {
  if (g_history_right.find(to) != g_history_right.end())
  {
   return false;
  }
 }
 return true;
}

bool can_cross(int index, const riverside& from, const riverside &to, int &first, int &second)
{
 int arry[COUPLE_COUNT * 2];
 covert(index, arry);
 int i = 0;
 int person_count = 0;
 for (i = 0; i < COUPLE_COUNT * 2; i++)
 {
  if (arry[i] == 1)
  {
   person_count++;
  }
 }
 if (person_count > 2) // 超载了
 {
  return false;
 }
 int person_in_boat[2] = {-1, -1};
 int j = 0;
 for (i = 0; i < COUPLE_COUNT * 2; i++)
 {
  if (arry[i] == 1)
  {
   if (from._person[i] == 0)
   {
    return false; // 岸边没这个人
   }
   person_in_boat[j++] = i;
  }
 }
 first = person_in_boat[0];
 second = person_in_boat[1];
 return can_cross(person_in_boat[0], person_in_boat[1], from, to);
}
void cross(int first, int second, riverside& from, riverside& to)
{
 if (no % 2 == 1) // 从左到右,记录下来过河前的状态
 {
  g_history_left.insert(from);
 }
 else // 从右到左,记录下过河前的状态
 {
  g_history_right.insert(from);
 } 
 if (first >= 0)
 {
  from._person[first] = 0;
  to._person[first] = 1;
 }
 if (second >= 0)
 {
  from._person[second] = 0;
  to._person[second] = 1;
 }
 from._has_boat = false;
 to._has_boat = true;
 
 record rcd;
 rcd._no = no;
 rcd._from = from;
 rcd._to = to;
 g_records.push_back(rcd);
}

int total_way = 1 << (2 * COUPLE_COUNT); // 一共 2 的 6 次方 种过河方案
void cross_river(riverside from, riverside to)
{
 no++;
 if (no % 2 == 1 && from.count() == 0) // 左岸已经没人了?
 {
  print_records();
  exit(12);
 }
 for (int i = 1; i < total_way; i++)
 {
  int first, second;
  if (!can_cross(i, from, to, first, second))
  {
   continue;
  }
  else
  {
   riverside f = from; // 把状态记录下来
   riverside t = to;
   cross(first, second, from, to);
   if (no % 2 == 1 && from.count() == 0) // 左岸已经没人了?
   {
    print_records();
    exit(12);
   }
   cross_river(to, from);
   from = f;
   to = t;
  }
 }
 g_records.pop_back();
 no--;
}
 
int main()
{
 riverside left, right;
 memset(&left, 1, sizeof(left));
 left._has_boat = true;
 
 memset(&right, 0, sizeof(right));
 right._has_boat = false;
 cross_river(left, right);
 printf("no way!\n\n");
}

你可能感兴趣的:([c++]阿拉伯夫妇过河)