经典算法-----农夫过河问题(深度优先搜索)

2023-10-09 04:44

本文主要是介绍经典算法-----农夫过河问题(深度优先搜索),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

农夫过河问题

1.问题描述

2.解决思路

位置编码

获取位置

判断是否安全 

 深度优先遍历(核心算法)

 3.完整代码


前言

        今天我们来解决一个有意思的问题,也就是农夫过河问题,可能这个问题我们小时候上学就听说过类似的问题,当时我们的解决方法就是一个一个列举,反复去找,但是在编程上对于这个问题的解决方法就是去通过回溯问题来找出全部的可能来解决这个问题,下面就一起来看看吧。

农夫过河问题

1.问题描述

一位农夫、一只狼、一只羊和一颗白菜都在河的一侧,他们想到河的另一侧。河中有一条船,一次只能载农夫和一个物体(狼或羊或白菜)。若农夫不在的时候,狼会吃掉羊,羊会吃掉白菜。问:该以什么样的方式才能将狼、羊和白菜完好的运到对岸?

在此之前我们先去通过自己思考一下怎么去过河,很简单:① 农夫把羊运到河岸1;② 农夫回来河岸0;③ 把白菜运往河岸1;④ 把羊运到河岸0;⑤ 把狼运到河岸1;⑥ 再回来河岸0;⑦ 把羊运往河岸1,最后完成过河。当然方法不止这一种,如果让你去写编程的话找出全部的方法,怎么写呢?

2.解决思路

位置编码

不妨设,当前其实岸为南岸即标记为0,目标岸北岸标记为1,然后分别通过一个二进制数来表示农夫、狼、羊、白菜的当前位置,比如1000(十进制为8)表示农夫当前位置在北岸,其他三个在南岸,以此类推0100(十进制为4)表示狼在北岸,0010(十进制为2)表示羊在北岸,0001(十进制为1)表示白菜在北岸,通过以上的规则我们可以明确的表示这4者的位置关系。

获取位置

        前面既然有了编码来表示位置关系,那么我假设当前4者的关系位置为 location,那如何获取到当前农夫、狼、羊、白菜的位置呢?

        很简单,只需要进行与运算就行了,比如location=1010,即表示农夫在北岸,狼在南岸,羊在北岸、白菜在南岸,那么我要知道农夫的位置只需要进行 location&8 的运算即可,得出的二进制数结果就是1000(十进制为8),即农夫在北岸.

//判断当前位置
int farmer(int loca) {//loca是表示当前4者的位置状态二进制数return ((loca & 8) == 8);
}
int wolf(int loca) {return ((loca & 4) ==4);
}
int sheep(int loca) {return ((loca & 2) ==2) ;
}
int cabbage(int loca) {return  ((loca & 1)==1);
}
判断是否安全 

既然有了位置,那就要去判断这种情况是否安全了,农夫不在狼和羊不可以在一起,农夫不在白菜和羊不可以在一起,

int isSafe(int loca) {int a, b, c, d;a = farmer(loca);b = wolf(loca);c = sheep(loca);d = cabbage(loca);if (a != c && c == d)//农夫不在白菜和羊不可以在一起,不安全return 0;else if (a != b && b == c)//农夫不在狼和羊不可以在一起,不安全return 0;elsereturn 1;
}
 深度优先遍历(核心算法)

        要想找到全部的运输方法那就需要去进行深度遍历,由于总共有16个状态,所以在此之前需要一个数组来储存表示当前的位置状态route[16],全部初始化为-1表示没有没有访问过这个运算过程,其中第一个状态也就是0000,最开始的时候4者都在南岸,那么route[0]=-2,表示最开始状态不需要去访问或者修改操作。

每次带一个物品过河,我们可以通过循环来实现二进制数的左移,从白菜开始向左移动,直到农夫,movers表示当前要进行移动的物体,这时候我们就需要算出如果这个物品移动之后,下一个状态nextlocation是否安全,如果满足条件的话,那就吧进行这一步移动操作,如果不安全那就换一个物体去移动,如果直到移动到农夫也不安全,那就进行回溯到上一步,从上一步重新开始移动下一个物体,以此类推,这就是一个深度遍历回溯的过程。

 

//深度遍历核心算法(回溯算法)
void process(int loca,int* route,int* count) {if (route[15] == -1) {//move表示要移动当前物体for (int move = 1; move <= 8; move <<=1) {//如果农夫与当前要移动的物体处于同一个岸的话if (((loca & 8)!=0) == ((loca & move)!=0)) {				int next_loca = loca ^ (8 | move);//获取下一个状态next_loca二进制数if (isSafe(next_loca) && route[next_loca] == -1) {//判断下一个状态是否安全,同时也没有访问过int next_route[16];for (int i = 0; i < 16; i++) {//把当前的路径复制一份,进入到下一步递归,以保证这一步的路径数据没被修改,进行回溯操作next_route[i] = route[i];}next_route[next_loca] = loca;//存储当前位置,进入到下一个process(next_loca, next_route,count);//回溯}}}}//如果route[15]储存了数据,那么都到达了对岸了,打印结果else {print(route, 15,count);puts("\n");}return;
}

 3.完整代码

#include<stdio.h>
#include<string.h>//北1  南0
//判断当前位置
int farmer(int loca) {return ((loca & 8) == 8);
}
int wolf(int loca) {return ((loca & 4) ==4);
}
int sheep(int loca) {return ((loca & 2) ==2) ;
}
int cabbage(int loca) {return  ((loca & 1)==1);
}int isSafe(int loca) {int a, b, c, d;a = farmer(loca);b = wolf(loca);c = sheep(loca);d = cabbage(loca);if (a != c && c == d)//农夫不在白菜和羊不可以在一起,不安全return 0;else if (a != b && b == c)//农夫不在狼和羊不可以在一起,不安全return 0;elsereturn 1;
}
//打印结果
void print(int* route,int status,int* count) {if (status == -2) {*count += 1;printf("第%d种方法:\n",*count);printf("start");return;}print(route, route[status],count);printf("->%d", status);
}//深度遍历核心算法(回溯算法)
void process(int loca,int* route,int* count) {if (route[15] == -1) {//move表示要移动当前物体for (int move = 1; move <= 8; move <<=1) {//如果农夫与当前要移动的物体处于同一个岸的话if (((loca & 8)!=0) == ((loca & move)!=0)) {				int next_loca = loca ^ (8 | move);//获取下一个状态next_loca二进制数if (isSafe(next_loca) && route[next_loca] == -1) {//判断下一个状态是否安全,同时也没有访问过int next_route[16];for (int i = 0; i < 16; i++) {//把当前的路径复制一份,进入到下一步递归,以保证这一步的路径数据没被修改,进行回溯操作next_route[i] = route[i];}next_route[next_loca] = loca;//存储当前位置,进入到下一个process(next_loca, next_route,count);//回溯}}}}//如果route[15]储存了数据,那么都到达了对岸了,打印结果else {print(route, 15,count);puts("\n");}return;
}int main() {int route[16];memset(route, -1, sizeof(route));route[0] = -2;int count = 0;//统计process(0,route,&count);
}

输出结果:

 以上就是今天的全部内容了,你们学会这个问题的解决方法吗?是不是很有意思呢?

分享一张壁纸: 

这篇关于经典算法-----农夫过河问题(深度优先搜索)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/170436

相关文章

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

Java内存泄漏问题的排查、优化与最佳实践

《Java内存泄漏问题的排查、优化与最佳实践》在Java开发中,内存泄漏是一个常见且令人头疼的问题,内存泄漏指的是程序在运行过程中,已经不再使用的对象没有被及时释放,从而导致内存占用不断增加,最终... 目录引言1. 什么是内存泄漏?常见的内存泄漏情况2. 如何排查 Java 中的内存泄漏?2.1 使用 J

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

解决systemctl reload nginx重启Nginx服务报错:Job for nginx.service invalid问题

《解决systemctlreloadnginx重启Nginx服务报错:Jobfornginx.serviceinvalid问题》文章描述了通过`systemctlstatusnginx.se... 目录systemctl reload nginx重启Nginx服务报错:Job for nginx.javas

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

解决Cron定时任务中Pytest脚本无法发送邮件的问题

《解决Cron定时任务中Pytest脚本无法发送邮件的问题》文章探讨解决在Cron定时任务中运行Pytest脚本时邮件发送失败的问题,先优化环境变量,再检查Pytest邮件配置,接着配置文件确保SMT... 目录引言1. 环境变量优化:确保Cron任务可以正确执行解决方案:1.1. 创建一个脚本1.2. 修