野人和传教士过河问题的C语言源代码

2008-11-14 17:40

问题:有3个传教士和3个野人要过河,只有一艘船,这艘船每次只能载2个人过河,且无论哪边野人的数量大于传教士的数量时,野人就会吃掉传教士。怎样让他们都安全过河?

C语言源代码:

#include <stdio.h>
#include <string.h>

#define STEP_MAX 20 //来回过河的次数
#define KIND_NUM 3 //每个种类的数量
#define BOAT_NUM 2 //船的载重量

typedef enum
{
BOAT_THIS,//船在本岸
BOAT_THAT,//船在对岸
} boat_t;

typedef enum
{
ROAD_GO,//过河
ROAD_COME,//回来
} road_t;

typedef struct
{
int ye;//对岸野人数量
int man;//对岸传教士数量
boat_t boat;//船是否在对岸
} state_t;//一种局面

typedef struct
{
int ye;//野人过河数量
int man;//传教士过河的数量
road_t road;//回来或过河
} step_t;//一个步骤


state_t states[STEP_MAX]={0};
step_t steps[STEP_MAX]={0};


//判断所有的野人和传教士是否都到了对岸
bool final(state_t s)
{
const state_t cs=
{
   KIND_NUM,
   KIND_NUM,
   BOAT_THAT
};
if(memcmp(&cs,&s,sizeof(state_t))==0)
{
   return true;
}
return false;
}

//是否会发生野人杀传教士
bool bad(state_t s)
{
if(((KIND_NUM-s.ye)>(KIND_NUM-s.man) && (KIND_NUM-s.man)>0)
   ||(s.ye>s.man && s.man>0))
{
   return true;
}
return false;
}

//第n种局面是否跟前面的相重复
bool repeat(state_t s[],int n)
{
int i;

for (i = 0; i < n; i++)
{//已经有这种情况了
   if (memcmp(&states[i], &states[n], sizeof(states[i])) == 0)
   {
    return true;
   }
}
return false;
}

void output(step_t steps[STEP_MAX],int n)
{
char *kinds[KIND_NUM]={"野人","传教士"};
char *routine[2]={"过河.","回来."};
int i;

for (i = 0; i < n; i++)
{   
   if((steps[i].ye * steps[i].man)>0)
   {
    printf("%d个%s和%d个%s%s\n",steps[i].ye,kinds[0],
     steps[i].man,kinds[1],routine[steps[i].road]);
   }
   else if(steps[i].ye>0)
   {
    printf("%d个%s%s\n",steps[i].ye,kinds[0],
     routine[steps[i].road]);
   }
   else if(steps[i].man>0)
   {
    printf("%d个%s%s\n",steps[i].man,kinds[1],
     routine[steps[i].road]);
   }
}
printf("\n");
}

//搜索过河方案(n表示过河次数)
void search(int n)
{
int i,j;

if(n>STEP_MAX)
{//步数限制太少了
   printf("Step Overflow!");
   return;
}
if(final(states[n]))
{//都到对岸了
   output(steps,n);
   return;
}
if(bad(states[n]))
{//发生了野人杀传教士了
   return;
}
if(repeat(states,n))
{//与前面的局面相重复了
   return;
}
if(states[n].boat==BOAT_THIS)
{//船在本岸
   for (i = 0; i <= BOAT_NUM && i<=(KIND_NUM-states[n].ye); i++)
   for (j = 0; j <= BOAT_NUM-i && j<=(KIND_NUM-states[n].man); j++)
   {
    if(i==0 &&j==0)
    {
     continue;
    }
    steps[n].ye=i;
    steps[n].man=j;
    steps[n].road=ROAD_GO;
    memcpy(&states[n+1], &states[n], sizeof(state_t));
    states[n+1].ye+=i;
    states[n+1].man+=j;
    states[n+1].boat=BOAT_THAT;
    search(n+1);
   }
}
else
{
   for (i = 0; i <= BOAT_NUM && i<=states[n].ye; i++)
   for (j = 0; j <= BOAT_NUM-i && j<=states[n].man; j++)
   {
    if(i==0 &&j==0)
    {
     continue;
    }
    steps[n].ye=i;
    steps[n].man=j;
    steps[n].road=ROAD_COME;
    memcpy(&states[n+1], &states[n], sizeof(state_t));
    states[n+1].ye-=i;
    states[n+1].man-=j;
    states[n+1].boat=BOAT_THIS;
    search(n+1);
   }
}
}

int main()
{
search(0);
return 0;
}

"传教士野人过河"是一个经典的算法设计题目,它涉及到了回溯法和状态转移的思想。这个问题描述的是有三个人(传教士野人和另一个传教士),其中两个人不会游泳,需要一艘船才能过河。但是每次只能带一个人过河,而且船没有人划的时候,必须有人留在对岸照看。 下面是一个简单的 C 语言版本的解决方案: ```c #include <stdio.h> #define MAX 4 // 情况总数 int dp[MAX][MAX]; // 动态规划数组 // 定义情况:0 - 无人过河,1 - 传教士1,2 - 野人,3 - 传教士2 void move(int a, int b) { printf("Move %d to the other side with %d\n", a + 1, b + 1); } int canCross(int state) { if (dp[state][state] != -1) return dp[state][state]; if (state == 3) { // 如果只剩传教士2,就直接过去 dp[state][state] = 1; return 1; } for (int i = 0; i <= state; i++) { // 遍历所有可能性 if (i == 1 || i == 2 || (state - i) == 1 || (state - i) == 2) { // 只考虑非冲突组合 if (canCross(i) && canCross(state - i)) { // 如果两边的人都能过河 dp[state][state] = 1; // 成功过河 move(state, i); // 记录行动 break; } } } return dp[state][state]; } int main() { memset(dp, -1, sizeof(dp)); // 初始化动态规划表 if (canCross(0)) { printf("Solution exists!\n"); } else { printf("No solution exists.\n"); } return 0; } ``` 这个代码首先初始化了一个动态规划表格 `dp` 来存储从初始状态到结束状态是否可行。函数 `move()` 描述了当前操作,`canCross()` 则通过递归尝试各种组合来判断是否能找到解决方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值