农夫过河问题——用户自定义初始状态和终止状态
来源:互联网 发布:2017阅读软件排行 编辑:程序博客网 时间:2024/06/08 10:30
问题描述
一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸,他要把这些东西全部运到北岸。他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。如果农夫在场,则狼不能吃羊,羊不能吃白菜;否则狼会吃羊,羊会吃白菜。所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而狼不吃白菜。要求给出农夫将所有的东西运过河的方案。
功能设计
- 用户输入起始状态,输入终止状态
- 判断输入是否合法
- 判断输入的状态是否安全
- 获取农夫、狼、白菜、羊的位置
- 输出运输方案
- 判断是否继续
实现思想
- 用4位二进制数顺序分别表示农夫、狼、白菜、羊的位置。0表示在南岸,1表示在北岸
- 使用按位与操作来确定每个角色所在位置的代码是0还是1。函数返回值为真,表示在北岸,否则在南岸
- 通过位置分布来判断当前状态是否安全。若状态安全返回1,否则返回0
- 将初始状态存入队列,并将存储状态路径的数组除初始状态位置置为当前状态所表示的十进制数外全部置为-1,表示未记录过路径
- 利用广度优先遍历从初始状态开始依次考虑农夫可能的选择,并判断过河的可能性。如果渡河后状态安全,则将其状态入队并将当前状态的索引记录到路径数组中,下标索引为后续状态值。若渡河后状态不安全,则继续考虑后面一个选择
- 若农夫、狼、白菜、羊这四种选择都考虑完成,则将队列中的元素出队,继续考虑新状态下农夫可能的选择和过河的可能性,直到队列为空或者路径数组下标索引值为终止状态的值被访问过了
- 从终止状态开始,根据路径数组的下标输出运输方案
代码
#include<stdio.h>#include<stdlib.h>#define MAXSIZE 16typedef int data;typedef struct queue{ data a[MAXSIZE]; int front,rear; //front队头,rear队尾}SeqQueue, * Seq;// 创建队列Seq create(){ Seq s; s = (Seq)malloc(sizeof(SeqQueue)); s->front = 0; s->rear = 0; return(s);}//判断是否为空int isEmpty(Seq s){ return( s->front == s->rear );}//判断是否为满int isFull(Seq s){ return( (s->rear+1)%MAXSIZE == s->front);}// 入队int insert(Seq s, data x){ if(isFull(s)){ printf("FULL!\n"); return 1; } else{ s->a[s->rear] = x; s->rear = (s->rear + 1) % MAXSIZE; return 0; }}// 出队int dele(Seq s, data *d){ if(isEmpty(s)){ printf("Empty!\n"); return 0; } else{ *d = s->a[s->front]; s->front = (s->front + 1) % MAXSIZE; return 1; }}//将数值进行转换int change(int s){ int f,w,c,g; f=s/1000; w=(s%1000)/100; c=((s%1000)%100)/10; g=((s%1000)%100)%10; s=f*8+w*4+c*2+g; return s;}//判断输入是否正确,正确返回1,否则,返回0int isTrue(int s){ int f,w,c,g; f=s/1000; w=(s%1000)/100; c=((s%1000)%100)/10; g=((s%1000)%100)%10; if(f >= 0 && w >= 0 && c >= 0 && g >= 0){ if(f < 2 && w < 2 && c < 2 && g < 2) return 1; else return 0; } else return 0;}//是否继续char isContinue(){ char choice; printf("\n是否继续?(Y/N)\n"); getchar(); scanf("%c",&choice); return choice;}//判断农夫的位置,与1000按位与int farmer(int state){ return ((state & 8) != 0);}//判断狼的位置,与0100按位与int wolf(int state){ return ((state & 4) != 0);}//判断白菜的位置,与0010按位与int cabbage(int state){ return ((state & 2) != 0);}//判断羊的位置,与0001按位与int goat(int state){ return ((state & 1) != 0);}// 若状态相容(安全)则返回1,否则返回0int isSafe(int state){ if((goat(state) == cabbage(state)) &&(goat(state) != farmer(state))) // 羊菜同岸且农夫不在场 return(0); if((goat(state) == wolf(state)) &&(goat(state) != farmer(state))) // 狼羊同岸且农夫不在场 return(0); return(1);}void river(int s,int o){ int route[16]; // 已经考虑的状态路径 int state; // 当前的状态(二进制) int newstate; // 当前的选择会导致的新的状态 int mover; // 农夫的选择(对应二进制位为1表示选中该乘客) int print[16]={0}; // 输出结果 int counter, i; Seq moveTo; moveTo = create(); // 创建“状态”队列 insert(moveTo,s); // 初始状态入队 for(i = 0; i < 16; i++){ if(i==s) route[i] = s; else route[i] = -1; //表示没有记录过路径 } if(s == o){ printf("\n您已成功到达目标状态!\n"); return; } while(!isEmpty(moveTo) && (route[o] == -1)){ if( !dele(moveTo, &state) ){ printf(" Empty! \n"); } // 依次考虑农夫可能的选择:携带羊、白菜和狼,以及农夫只身渡河 //向左位移,mover分别为0001,0010,0100,1000,依次判断过河的可能性 for( mover = 1; mover<= 8; mover <<= 1 ){ if(((state & 8) != 0) == ((state & mover) != 0)){ // 如果农夫与当前选择在河岸的同一侧 newstate = state^( 8|mover ); // 渡河后的情况 if(isSafe(newstate) && (route[newstate] == -1)){ // 如果渡河后状态安全,则将其状态入队 route[newstate] = state; // 将当前状态的索引记录到路径数组中(下标索引为后续状态值) insert(moveTo, newstate); } } } } // 输出过河策略,0表示在南岸 1表示在北岸 if(route[o] != -1){ counter = 0; for(state = o; state != s; state = route[state]){ print[counter] = state; counter++; } print[counter] = s; for(i = counter;i>0;i--){ state= print[i]; newstate = print[i-1]; if(farmer(state) == 0){ printf(" 南岸 | | 北岸\n"); } else{ printf(" 北岸 | | 南岸\n"); } switch(state^newstate ){ case 12: printf(" ----------狼---------- \n"); break; case 10: printf(" ---------白菜--------- \n"); break; case 9: printf(" ----------羊---------- \n"); break; default: printf(" ------什么也不带------ \n"); break; } } } else printf("\n无解!\n");}int main(){ int s,o,s1,o1; //s1,o1为测试输入是否正确 int f,w,c,g; char choice; printf("\n 农夫过河问题\n\n"); do{ do{ //当初始状态输入不正确时重新输入 printf("\n请输入初始状态(农夫 狼 白菜 羊 0表示在南岸,1表示在北岸):\n\n"); scanf("%d",&s); s1=s; if(isTrue(s1) == 1){ s = change(s); if(isSafe(s) == 0){ printf("\n该状态不安全!\n"); choice = isContinue(); if(choice == 'N' || choice == 'n') break; } else{ do{ //当终止状态输入不正确时重新输入 printf("\n请输入终止状态(农夫 狼 白菜 羊 0表示在南岸,1表示在北岸):\n\n"); scanf("%d",&o); o1=o; if(isTrue(o1) == 1){ o = change(o); if(isSafe(o) == 0) printf("\n该状态不安全!\n"); else river(s,o); choice = isContinue(); if(choice == 'N' || choice == 'n') break; } else printf("\n输入错误!请重新输入!\n"); }while(isTrue(o1)==0); } } else printf("\n输入错误!请重新输入!\n"); if(choice == 'N' || choice == 'n') break; }while(isTrue(s1) == 0); }while(choice == 'Y' || choice == 'y'); return 0;}
界面
阅读全文