python 回溯法 子集树模板 系列 —— 19、野人与传教士问题

简介: 问题在河的左岸有N个传教士、N个野人和一条船,传教士们想用这条船把所有人都运过河去,但有以下条件限制:(1)修道士和野人都会划船,但船每次最多只能运M个人;(2)在任何岸边以及船上,野人数目都不能超过修道士,否则修道士会被野人吃掉。

问题

在河的左岸有N个传教士、N个野人和一条船,传教士们想用这条船把所有人都运过河去,但有以下条件限制:

(1)修道士和野人都会划船,但船每次最多只能运M个人;

(2)在任何岸边以及船上,野人数目都不能超过修道士,否则修道士会被野人吃掉。

假定野人会服从任何一种过河安排,请规划出一个确保修道士安全过河的计划。

分析

百度一下,网上全是用左岸的传教士和野人人数以及船的位置这样一个三元组作为状态,进行考虑,千篇一律。

我换了一种考虑,只考虑船的状态。

船的状态:(x, y) x表示船上x个传教士,y表示船上y个野人,其中 |x|∈[0, m], |y|∈[0, m], 0<|x|+|y|<=m, x*y>=0, |x|>=|y|

船从左到右时,x,y取非负数。船从右到左时,x,y取非正数

解的编码:[(x0,y0), (x1,y1), ..., (xp,yp)] 其中x0+x1+...+xp=N, y0+y1+...+yp=N

解的长度不固定,但一定为奇数

开始时左岸(N, N), 右岸(0, 0)。最终时左岸(0, 0), 右岸(N, N)

由于船的合法状态是动态的、二维的。因此,使用一个函数get_states()来专门生成其状态空间,使得主程序更加清晰。

代码

n = 3  # n个传教士、n个野人
m = 2  # 船能载m人

x = [] # 一个解,就是船的一系列状态
X = [] # 一组解

is_found = False # 全局终止标志


# 计算船的合法状态空间(二维)
def get_states(k): # 船准备跑第k趟
    global n, m, x
    
    if k%2==0:  # 从左到右,只考虑原左岸人数
        s1, s2 = n - sum(s[0] for s in x), n - sum(s[1] for s in x)
    else:       # 从右到左,只考虑原右岸人数(将船的历史状态累加可得!!!)
        s1, s2 = sum(s[0] for s in x), sum(s[1] for s in x)
    
    for i in range(s1 + 1):
        for j in range(s2 + 1):
            if 0 < i+j <= m and (i*j == 0 or i >= j):
                yield [(-i,-j), (i,j)][k%2==0]   # 生成船的合法状态


# 冲突检测
def conflict(k): # 船开始跑第k趟
    global n, m, x
    
    # 若船上载的人与上一趟一样(会陷入死循环!!!!)
    if k > 0 and x[-1][0] == -x[-2][0] and x[-1][1] == -x[-2][1]:
        return True
        
    # 任何时候,船上传教士人数少于野人,或者无人,或者超载(计算船的合法状态空间时已经考虑到了。)
    #if 0 < abs(x[-1][0]) < abs(x[-1][1]) or x[-1] == (0, 0) or abs(sum(x[-1])) > m:
    #    return True
    
    # 任何时候,左岸传教士人数少于野人
    if 0 < n - sum(s[0] for s in x) < n - sum(s[1] for s in x):
        return True
    
    # 任何时候,右岸传教士人数少于野人
    if 0 < sum(s[0] for s in x) < sum(s[1] for s in x):
        return True
    
    return False # 无冲突
    
    
    
# 回溯法
def backtrack(k): # 船准备跑第k趟
    global n, m, x, is_found
    
    if is_found: return  # 终止所有递归
    
    if n - sum(s[0] for s in x) == 0 and n - sum(s[1] for s in x) == 0: # 左岸人数全为0
        print(x)
        is_found = True
    else:
        for state in get_states(k): # 遍历船的合法状态空间
            x.append(state)
            if not conflict(k):
                backtrack(k+1) # 深度优先
            x.pop()   # 回溯


# 测试
backtrack(0)

效果图

img_d61dd7bbd505e09713c4c0f0f5631f3b.jpg

解的解释,从上往下看:
img_118371eccde25ce7027707255f85f42e.jpg

一个结论

貌似只有满足m = n-1,此问题才有解

目录
相关文章
|
Python 数据可视化
python 回溯法 子集树模板 系列 —— 1、8 皇后问题
问题 8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 分析 为了简化问题,考虑到8个皇后不同行,则每一行放置一个皇后,每一行的皇后可以放置于第0、1、2、...、7列,我们认为每一行的皇后有8种状态。
960 0
|
1天前
|
机器学习/深度学习 人工智能 数据可视化
Python比较适合哪些场景的编程?
Python比较适合哪些场景的编程?
14 7
|
6天前
|
数据挖掘 索引 Python
Python数据挖掘编程基础3
字典在数学上是一个映射,类似列表但使用自定义键而非数字索引,键在整个字典中必须唯一。可以通过直接赋值、`dict`函数或`dict.fromkeys`创建字典,并通过键访问元素。集合是一种不重复且无序的数据结构,可通过花括号或`set`函数创建,支持并集、交集、差集和对称差集等运算。
15 9
|
2天前
|
存储 数据处理 开发者
深入浅出:Python编程基础与实战技巧
【9月更文挑战第32天】本文将引导读者从零开始,掌握Python编程语言的核心概念,并通过实际代码示例深入理解。我们将逐步探索变量、数据结构、控制流、函数、类和异常处理等基本知识,并结合实用案例,如数据处理、文件操作和网络请求,提升编程技能。无论您是初学者还是有一定经验的开发者,这篇文章都能帮助您巩固基础,拓展视野。
|
1天前
|
大数据 Python
Python 高级编程:深入探索高级代码实践
本文深入探讨了Python的四大高级特性:装饰器、生成器、上下文管理器及并发与并行编程。通过装饰器,我们能够在不改动原函数的基础上增添功能;生成器允许按需生成值,优化处理大数据;上下文管理器确保资源被妥善管理和释放;多线程等技术则助力高效完成并发任务。本文通过具体代码实例详细解析这些特性的应用方法,帮助读者提升Python编程水平。
18 5
|
2天前
|
数据采集 机器学习/深度学习 人工智能
Python编程之旅:从基础到精通
【9月更文挑战第32天】本文将带你进入Python的世界,从基础语法到高级特性,再到实战项目,让你全面掌握Python编程技能。无论你是初学者还是有一定基础的开发者,都能在这篇文章中找到适合自己的学习路径和方法。让我们一起踏上Python编程之旅,开启一段充满挑战和乐趣的学习历程吧!
|
5天前
|
存储 开发者 Python
探索Python编程的奥秘
【9月更文挑战第29天】本文将带你走进Python的世界,通过深入浅出的方式,解析Python编程的基本概念和核心特性。我们将一起探讨变量、数据类型、控制结构、函数等基础知识,并通过实际代码示例,让你更好地理解和掌握Python编程。无论你是编程新手,还是有一定基础的开发者,都能在这篇文章中找到新的启示和收获。让我们一起探索Python编程的奥秘,开启编程之旅吧!
|
6天前
|
人工智能 小程序 API
文字转语音神器+Python编程搞定语音报时小程序
文字转语音神器+Python编程搞定语音报时小程序
11 2
|
6天前
|
Python
Python编程的循环结构小示例(二)
Python编程的循环结构小示例(二)
|
6天前
|
算法 Python
Python编程的函数—内置函数
Python编程的函数—内置函数
10 0
下一篇
无影云桌面