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();//停顿函数 }
实验结果: