1.题目简述:有N个传教士和N个野人要过河,现在有一条船只能承载N个人(包括野人),在任何时刻,如果有野人和传教士在一起,必须要求传教士的人数多于或等于野人的人数。
2.解答描述:这题我通过人工只能基于生产式系统解答,其实就是算法中说的深度优先搜索算法。在自己归纳策略集的时候发现当N=1时一次就过去了,当N=2时只有两条规则,当N=3时有5条规则,当N=4时有9条规则,当N=5时有14条规则,所以取N=3时比较便于表达又有代表性(当然河对岸的规则相同)。
3.具体代码:
代码如下,所有思想基本标注:
- #include "stdafx.h"
- #include<process.h>
- #include<stdio.h>
- #include <stdlib.h>
- #define NULL 0
- #define TRUE 1
- #define FALSE 0
- #define OK 1
- #define ERROR 0
- typedef struct
- {
- int m; //传教士人数
- int c; //野人人数
- int b; //船的位置变量
- }QElemType; /* 定义队列的数据元素类型QElemType为结构体类型 */
- typedef struct _Rule
- {
- int m;//传教士人数
- int c;//野人人数
- }Rule;
- Rule rule[5] = {{1,1}, {1,0}, {0,1}, {2,0}, {0,2}}; // 规则集e
- typedef struct QNode
- {
- QElemType data;
- struct QNode *next;
- }QNode,*QueuePtr;//节点结构体
- typedef struct
- {
- QueuePtr front,rear; //队头、队尾指针
- }LinkQueue;
- /* 构造一个空队列Q */
- void InitQueue(LinkQueue *Q)
- {
- (*Q).front=(*Q).rear=(QueuePtr)malloc(sizeof(QNode));
- if(!(*Q).front)
- exit(0);
- (*Q).front->next=NULL;
- }
- /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
- int DeQueue(LinkQueue *Q,QElemType *e)
- {
- QueuePtr p;
- if((*Q).front==(*Q).rear)
- return ERROR;
- p=(*Q).front->next;
- *e=p->data;
- (*Q).front->next=p->next;
- if((*Q).rear==p)
- (*Q).rear=(*Q).front;
- free(p);
- return OK;
- }
- /* 插入元素e为Q的新的队尾元素 */
- void EnQueue(LinkQueue *Q,QElemType e)
- {
- QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
- if(!p)
- exit(0);
- p->data=e;
- p->next=NULL;
- (*Q).rear->next=p;
- (*Q).rear=p;
- }
- /* 若Q队列,有给定节点返回true,否则返回false */
- int cmp(LinkQueue *Q,QElemType e){
- QueuePtr p=(*Q).front->next;
- while(p!=NULL){
- if(p->data.m==e.m&&p->data.c==e.c&&p->data.b==e.b)
- return TRUE;
- else p=p->next;
- }
- return FALSE;
- }
- /* 若Q为空队列,则返回TRUE,否则返回FALSE */
- int QueueEmpty(LinkQueue Q)
- {
- if(Q.front->next==NULL)
- return TRUE;
- else
- return FALSE;
- }
- void main(){
- LinkQueue open,closed; //定义首尾指针
- QueuePtr p; //定义节点指针
- int i;
- InitQueue(&open); //初始化open队列
- InitQueue(&closed); //初始化closed队列
- QElemType s={3,3,1},e,e1; //初始化初始节点s,3个传教士,3个野人,船在左岸
- EnQueue(&open,s); //将s入列
- while(!QueueEmpty(open)){
- DeQueue(&open,&e);
- EnQueue(&closed,e);
- if(e.m==0&&e.c==0&&e.b==0)//判断条件控制策略是否结束
- {printf("成功!");
- continue;}
- for(i=0;i<5;i++) //因为控制策略只有5步所以这里循环5次
- {e1.m=e.m,e1.c=e.c,e1.b=e.b;
- if(e1.b==1)//船在左岸,对数据库做减法
- {
- e1.m=e1.m-rule[i].m;
- e1.c=e1.c-rule[i].c;
- e1.b=0;
- if((e1.m>=e1.c||e1.m==0)&&((3-e1.m)>=(3-e1.c)||(3-e1.m)==0)&&e1.m<=3&&e1.c<=3&&e1.m>=0&&e1.c>=0)
- {if(!cmp(&closed,e1))
- if(!cmp(&open,e1))
- EnQueue(&open,e1);}//需要解决元素问题
- }//if
- else //船在右岸,对数据库做加法
- {e1.m=e1.m+rule[i].m;
- e1.c=e1.c+rule[i].c;
- e1.b=1;
- if((e1.m>=e1.c||e1.m==0)&&((3-e1.m)>=(3-e1.c)||(3-e1.m)==0)&&e1.m<=3&&e1.c<=3&&e1.m>=0&&e1.c>=0)
- {if(!cmp(&closed,e1))
- if(!cmp(&open,e1))
- EnQueue(&open,e1);}//需要解决元素重复问题
- }//else
- }//for
- }//while
- p=closed.front;//指向结果集
- p=p->next;
- printf("\n");
- while(p!=NULL){
- printf("%d,%d,%d\n",p->data.m,p->data.c,p->data.b);//打印解决结果
- p=p->next;
- }
- getchar();//停顿函数
- }
#include "stdafx.h"
#include<process.h>
#include<stdio.h>
#include <stdlib.h>
#define NULL 0
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef struct
{
int m; //传教士人数
int c; //野人人数
int b; //船的位置变量
}QElemType; /* 定义队列的数据元素类型QElemType为结构体类型 */
typedef struct _Rule
{
int m;//传教士人数
int c;//野人人数
}Rule;
Rule rule[5] = {{1,1}, {1,0}, {0,1}, {2,0}, {0,2}}; // 规则集e
typedef struct QNode
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;//节点结构体
typedef struct
{
QueuePtr front,rear; //队头、队尾指针
}LinkQueue;
/* 构造一个空队列Q */
void InitQueue(LinkQueue *Q)
{
(*Q).front=(*Q).rear=(QueuePtr)malloc(sizeof(QNode));
if(!(*Q).front)
exit(0);
(*Q).front->next=NULL;
}
/* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
int DeQueue(LinkQueue *Q,QElemType *e)
{
QueuePtr p;
if((*Q).front==(*Q).rear)
return ERROR;
p=(*Q).front->next;
*e=p->data;
(*Q).front->next=p->next;
if((*Q).rear==p)
(*Q).rear=(*Q).front;
free(p);
return OK;
}
/* 插入元素e为Q的新的队尾元素 */
void EnQueue(LinkQueue *Q,QElemType e)
{
QueuePtr p=(QueuePtr)malloc(sizeof(QNode));
if(!p)
exit(0);
p->data=e;
p->next=NULL;
(*Q).rear->next=p;
(*Q).rear=p;
}
/* 若Q队列,有给定节点返回true,否则返回false */
int cmp(LinkQueue *Q,QElemType e){
QueuePtr p=(*Q).front->next;
while(p!=NULL){
if(p->data.m==e.m&&p->data.c==e.c&&p->data.b==e.b)
return TRUE;
else p=p->next;
}
return FALSE;
}
/* 若Q为空队列,则返回TRUE,否则返回FALSE */
int QueueEmpty(LinkQueue Q)
{
if(Q.front->next==NULL)
return TRUE;
else
return FALSE;
}
void main(){
LinkQueue open,closed; //定义首尾指针
QueuePtr p; //定义节点指针
int i;
InitQueue(&open); //初始化open队列
InitQueue(&closed); //初始化closed队列
QElemType s={3,3,1},e,e1; //初始化初始节点s,3个传教士,3个野人,船在左岸
EnQueue(&open,s); //将s入列
while(!QueueEmpty(open)){
DeQueue(&open,&e);
EnQueue(&closed,e);
if(e.m==0&&e.c==0&&e.b==0)//判断条件控制策略是否结束
{printf("成功!");
continue;}
for(i=0;i<5;i++) //因为控制策略只有5步所以这里循环5次
{e1.m=e.m,e1.c=e.c,e1.b=e.b;
if(e1.b==1)//船在左岸,对数据库做减法
{
e1.m=e1.m-rule[i].m;
e1.c=e1.c-rule[i].c;
e1.b=0;
if((e1.m>=e1.c||e1.m==0)&&((3-e1.m)>=(3-e1.c)||(3-e1.m)==0)&&e1.m<=3&&e1.c<=3&&e1.m>=0&&e1.c>=0)
{if(!cmp(&closed,e1))
if(!cmp(&open,e1))
EnQueue(&open,e1);}//需要解决元素问题
}//if
else //船在右岸,对数据库做加法
{e1.m=e1.m+rule[i].m;
e1.c=e1.c+rule[i].c;
e1.b=1;
if((e1.m>=e1.c||e1.m==0)&&((3-e1.m)>=(3-e1.c)||(3-e1.m)==0)&&e1.m<=3&&e1.c<=3&&e1.m>=0&&e1.c>=0)
{if(!cmp(&closed,e1))
if(!cmp(&open,e1))
EnQueue(&open,e1);}//需要解决元素重复问题
}//else
}//for
}//while
p=closed.front;//指向结果集
p=p->next;
printf("\n");
while(p!=NULL){
printf("%d,%d,%d\n",p->data.m,p->data.c,p->data.b);//打印解决结果
p=p->next;
}
getchar();//停顿函数
}
实验结果: