|
| 1 | +#!/usr/local/bin/python3 |
| 2 | +########################################## |
| 3 | +# 问题: |
| 4 | +# 有一条河,河边有一个猎人牵着一头狼,一个男人带着两个小男孩,还有一个女人带着两个小女孩 |
| 5 | +# 如果猎人离开,狼就会把所有人吃掉 |
| 6 | +# 如果男人离开,女人会把两个小男孩掐死 |
| 7 | +# 如果女人离开,男人会把两个小女孩掐死 |
| 8 | +# 河里有一条船,船上只能乘坐两人(狼算一人),只有猎人、男人、女人会划船 |
| 9 | +# 如何使他们全部过河? |
| 10 | + |
| 11 | + |
| 12 | +########################################## |
| 13 | +# 思路: |
| 14 | +# 与农夫过河问题类似,参考 https://www.zhihu.com/question/29968331 |
| 15 | +# 一共有 8 个人(包含狼),每个人有两个状态:分别为在河的一侧,以及在河的另一侧 |
| 16 | +# 这两种状态可用一个二进制位表示,0 代表在河的一侧,1 代表在河的另一侧 |
| 17 | +# 则可用一个 8 位二进制数表示这 8 个人的状态,进行组合,一共有 256 个状态 |
| 18 | +# 通过坐船,可以使一种状态转变为另一种状态 |
| 19 | +# 如果状态从 00000000 转换到 11111111, 则说明所有人均到达河的另一侧 |
| 20 | +# 将两种可以相互转变的状态相互连接,可以构建出一个用于表示状态转移情况的图 |
| 21 | +# 在图中找到一条从 00000000 到 11111111 的路径,即可解决该问题 |
| 22 | + |
| 23 | + |
| 24 | +########################################## |
| 25 | +# 数据存储格式: |
| 26 | +# 使用 8 位二进制数进行表示,从高位到低位分别表示男人,男孩,男孩,女人,女孩,女孩,狼的状态 |
| 27 | + |
| 28 | + |
| 29 | +# 船允许的所有状态,1 代表在船上 |
| 30 | +# 将两个状态进行异或操作,发生变化的位就会置一,则说明置一的位所对应的人是在船上 |
| 31 | +# 但是,船的行驶是有方向的,例如从 10000000 到 01000000 的状态是无法通过一次乘船达到的 |
| 32 | +# 所以需要进行进一步判断,具体方法见下面的代码 |
| 33 | +boatAllowedStates = [ |
| 34 | + 0b11000000, 0b10100000, 0b00011000, 0b00010100, 0b10000010, |
| 35 | + 0b01000010, 0b00100010, 0b00010010, 0b00001010, 0b00000110, |
| 36 | + 0b00000011, 0b10010000, 0b10000000, 0b00010000, 0b00000010 |
| 37 | +] |
| 38 | + |
| 39 | +# 以邻接矩阵的形式存储可能的状态转移情况 |
| 40 | +# 矩阵的索引就是用于表示状态的 8 位二进制数(0~255) |
| 41 | +statesGraph = [([0] * 256) for i in range(256)] # 创建 256x256 二维数组 |
| 42 | + |
| 43 | +# 判断是否为危险的状态 |
| 44 | +def is_dangerous(x): |
| 45 | + # 第一种不安全的情况:男人不在,女人在,男孩在 |
| 46 | + if ( x & 0b10000000) == 0 and ( x & 0b00010000) != 0 and ( x & 0b01100000) != 0: |
| 47 | + return True |
| 48 | + if (~x & 0b10000000) == 0 and (~x & 0b00010000) != 0 and (~x & 0b01100000) != 0: |
| 49 | + return True |
| 50 | + # 第二种不安全的情况:女人不在,男人在,女孩在 |
| 51 | + if ( x & 0b00010000) == 0 and ( x & 0b10000000) != 0 and ( x & 0b00001100) != 0: |
| 52 | + return True |
| 53 | + if (~x & 0b00010000) == 0 and (~x & 0b10000000) != 0 and (~x & 0b00001100) != 0: |
| 54 | + return True |
| 55 | + # 第三种不安全的情况:猎人不在,狼在,其他人在 |
| 56 | + if ( x & 0b00000010) == 0 and ( x & 0b00000001) != 0 and ( x & 0b11111100) != 0: |
| 57 | + return True |
| 58 | + if (~x & 0b00000010) == 0 and (~x & 0b00000001) != 0 and (~x & 0b11111100) != 0: |
| 59 | + return True |
| 60 | + return False |
| 61 | + |
| 62 | +# 构建该邻接矩阵 |
| 63 | +for x in range(0, 256): |
| 64 | + if is_dangerous(x): # 判断 x 的状态是否危险 |
| 65 | + continue |
| 66 | + for y in range(0, 256): |
| 67 | + if is_dangerous(y): # 判断 y 的状态是否危险 |
| 68 | + continue |
| 69 | + # 如果通过坐船,可以使状态 x 转变为状态 y, 则使矩阵对应位置为 1 |
| 70 | + tmp = x ^ y |
| 71 | + if tmp in boatAllowedStates: |
| 72 | + # 进一步判断,排除类似 10000000 到 01000000 的情况 |
| 73 | + if tmp & x ^ tmp == 0 or tmp & x ^ tmp == tmp: |
| 74 | + statesGraph[x][y] = 1 # 连接图中能够能够转换的状态 |
| 75 | + |
| 76 | +# 通过图的深度优先搜索算法,找出一条从 0b00000000 到 0b11111111 的路径 |
| 77 | +# ⚠️TODO: |
| 78 | +# 此时仅仅是找到了一条能够到达的路径,不一定是最佳的结果 |
| 79 | +# 可考虑直接找出所有可行的结果 |
| 80 | +# 或使用最短路径算法,找出乘船次数最少的结果 |
| 81 | +visited = [0] * 256 # 用在图的深度优先搜索中,已访问的状态 |
| 82 | +path = [257] * 256 # 用于存储路径,数组内元素为下一个状态, 257 代表尚未初始化 |
| 83 | + |
| 84 | +def dfs_with_path(src): |
| 85 | + visited[src] = 1 |
| 86 | + for i in range(0, 256): |
| 87 | + if statesGraph[src][i] == 1 and visited[i] == 0 and visited[0b11111111] == 0: |
| 88 | + path[src] = i |
| 89 | + dfs_with_path(i) |
| 90 | + |
| 91 | +dfs_with_path(0b00000000) |
| 92 | + |
| 93 | +# 将路径显示在屏幕上 |
| 94 | +tmp_path = 0 |
| 95 | +while tmp_path != 0b11111111: |
| 96 | + print('{0:08b}'.format(tmp_path)) |
| 97 | + tmp_path = path[tmp_path] |
| 98 | + if(tmp_path == 257): |
| 99 | + # 257 在本程序中代表未初始化的值,如果遇到 257,说明没找到这样的路径 |
| 100 | + print("Not found!") |
| 101 | + exit() |
| 102 | +print('{0:08b}'.format(0b11111111)) |
| 103 | +print("Found!") |
0 commit comments