目录

1. 问题描述

2. 解题分析

2.1 状态表示方法

2.2 DFS

2.3 遍历下一个状态

2.4 交叉判断

3. 代码

4. 后记


1. 问题描述

即便系得很紧,鞋带有时候还是免不了会松掉。运动鞋的鞋带有很多时髦的系法。下面看看这些系法里,鞋带是如何穿过一个又一个鞋带孔的。如下图所示的这几种依次穿过 12 个鞋带孔的系法很有名(这里不考虑鞋带穿过鞋带孔时是自外而内还是自内而外)。

这里指定鞋带最终打结固定的位置如上图中的前两种系法所示,即固定在最上方(靠近脚腕)的鞋带孔上,并交错使用左右的鞋带孔。

问题:鞋带交叉点最多时的交叉点个数。譬如上图左侧的系法是 5 个,正中间的系法是 9 个。

2. 解题分析

深度优先路径遍历问题。

本系列中已经出现过很多类似问题,比如Q24, Q23, Q18, Q14, just to name a few. 此类问题都可以直接套用类似的框架套路,除了一些problem-specific的细节需要针对个别问题具体处理,比如说,如何进行状态表示,如何给出下一个状态的遍历列表,等等。

本文在路径遍历的基础上进一步要求找出各种系法(即各种路径)中的交叉点数,而不仅仅是给出可能的路径数,这就要求(1)在路径遍历的同时要记住每条路径的历史;(2)对每条路径中的交叉点进行数数。

由于要记住每条路径的历史,所以memoization技巧不能用了(待确认).

2.1 状态表示方法

考虑用left和right两个列表来表示当前状态,分别表示左右两列剩下的鞋带孔。由于鞋带孔不能重复穿过,基于这种表示方法,在深度搜索过程中也免去了“是否已经访问过”的判断。

除此之外,还需要:

  1. 指明下次应该轮到穿哪一列。activeCol: 0—left column; 1—right column
  2. 上一次穿过的是哪个孔:lastPole

必须从左列第一个孔开始(从右列开始结果一样,假定这种对称的系法算同一种系法),而且最后一个必须是右列第一个孔,因此初始状态为:

left = [1,2,3,4,5], right=[1,2,3,4,5], activeCol=1, lastPole=0

右端最后一个孔是确定性的,因此在深度优先搜索中不进行处理(因此以上right中不包括0),但是在搜索到终点进行交叉点计数时要把最后穿过right-pole-0的edge加入pathHist(参加下一节流程)。

2.2 DFS

2.3 遍历下一个状态

在问题中即寻找可以下一个穿过的鞋带孔。这里需要分从左到右和从右到左的处理,activeCol即为此目的而设。activeCol为1表示接下来是要从左到右;activeCol为0表示接下来是要从右到左。另外,还需要知道上一次穿过的孔(即lastPole),这样才能决定从哪个孔到哪个孔。

2.4 交叉判断

简单地来说,每个edge由两个数(x,y)决定,x是左边鞋带孔位置,y是右边鞋带孔位置。两个edges的x坐标的相对大小和y坐标的相对大小关系是反的的话,则表示交叉。参见isCross()。

3. 代码

# -*- coding: utf-8 -*-
"""
Created on Sun Sep 12 13:54:43 2021@author: chenxy
"""
import sys
import time
import datetime
import math
# import random
from   typing import List
# from   queue import Queue
# from   collections import deque
import itertools as itmaxNumCross = 0
maxPath     = []def isCross(edge1, edge2)->int:"""Judge whether two edges crossParameters----------edge1 : tuple, (x,y)x represents the line number in left columny represents the line number in righg columnedge2 : Same as edge1Returns: int. 0: not cross, 1: cross-------"""if (edge1[0] - edge2[0]) * (edge1[1] - edge2[1]) < 0:return 1else:return 0def explore(left:List, right:List, activeCol:int, lastPole:int, pathHist:List)->int:"""Explore the total number of paths under the condition of "left" and "right", whichcontains the left poles in the left and right column, repectively.Parameters----------left :  List. The left poles in the left column        right : List. The left poles in the right column                activeCol: int, indicate which column should be used in the next step. 0: left, 1, rightReturns-------int:    The total number of paths traversing all the poles in interleaving way."""global maxNumCrossglobal maxPathif len(left)==0 and len(right)==0:# find one path, and count the number of crosses# Firstly, add the last edge from lastPole to the first pole of right polepathHist.append([lastPole,0])        numCross = 0for edges in it.combinations(pathHist, 2):edge1 = edges[0]edge2 = edges[1]numCross += isCross(edge1,edge2)        if numCross > maxNumCross:maxNumCross = numCrossmaxPath = pathHist.copy()pathHist.remove([lastPole,0])return 1count = 0if activeCol == 0:nxtActiveCol = 1for pole in left:nxtLeft = left.copy()nxtLeft.remove(pole)pathHist.append([pole,lastPole])count += explore(nxtLeft,right,nxtActiveCol,pole,pathHist)pathHist.remove([pole,lastPole])else:nxtActiveCol = 0for pole in right:nxtRight = right.copy()nxtRight.remove(pole)pathHist.append([lastPole,pole])count += explore(left,nxtRight,nxtActiveCol,pole,pathHist)        pathHist.remove([lastPole,pole])return count
if __name__ == '__main__':        # tStart  = time.perf_counter()# print(explore([1],[1],1,0,[]))            # tCost   = time.perf_counter() - tStart# print('Number of cross = {0}, tCost = {1:6.3f}(sec)'.format(maxNumCross, tCost))# print(maxPath)tStart  = time.perf_counter()print(explore([1,2,3,4,5],[1,2,3,4,5],1,0,[]))            tCost   = time.perf_counter() - tStartprint('Number of cross = {0}, tCost = {1:6.3f}(sec)'.format(maxNumCross, tCost))print(maxPath)

运行结果:

14400
        Number of cross = 45, tCost =  0.300(sec)
        [[0, 5], [1, 5], [1, 4], [2, 4], [2, 3], [3, 3], [3, 2], [4, 2], [4, 1], [5, 1], [5, 0]]

第一个数字是可能系法总数,最后一个结果是具有最大交叉点的系法的鞋带孔穿越顺序。

4. 后记

其实,总的可能系法总数可以很简单地直接计算出来,即 。理由就留给小伙伴们自己思考了。本题中因为要给具体的路径历史(即鞋带孔穿越顺序),所以必须做穷尽的搜索。但是能直接计算出以上总的可能路径总数,对于程序调试是有帮助的。

上一篇:Q24: 完美的三振出局

下一篇:​​​​​​​Q26: 高效的立体停车场​​​​​​​

本系列总目录参见:程序员的算法趣题:详细分析和Python全解

程序员的算法趣题Q25: 时髦的鞋带系法相关推荐

  1. php算法求出一个数可以被分解成多少个_程序员的算法趣题

    计算机的世界每天都在发生着深刻的变化.新操作系统的发布.CPU性能的提升.智能手机和平板电脑的流行.存储介质的变化.云的普及--这样的变化数不胜数. 在这样日新月异的时代中,"算法" ...

  2. 程序员的算法趣题 python3 - (5)

    注:以下题目来自<程序员的算法趣题>– [日]增井敏克著,原书解法主要用Ruby实现,最近在学Python,随便找点东西写写当做练习,准备改成Python3实现,顺便增加一些自己的理解. ...

  3. LeetBook《程序员的算法趣题》Q18---水果酥饼日

    <程序员的算法趣题>-(日)增井敏克 , 书中为69 道数学谜题编写了解题程序, 编程语言为:Ruby,JavaScript,C语言. Q18 水果酥饼日   日本每月的 22 日是水果酥 ...

  4. 程序员的算法趣题Q50: 完美洗牌

    目录 1. 问题描述 2. 解题分析 2.1 思路1 2.2 思路2 3. 代码及测试 4. 后记 1. 问题描述 问题:对2n张牌洗牌,并求当1<=n<=100时,一共有多少个n可以使得 ...

  5. 程序员的算法趣题Q09: 落单的男女

    目录 1. 问题描述 2. 解题分析 3. 代码及测试 4. 思考 1. 问题描述 人们聚集在某个活动会场上,根据到场顺序排成一排等待入场,活动的主办人员,想把人们从队列的某个位置分成两组,想要让分开 ...

  6. 程序员的算法趣题Q55: 平分蛋糕

    目录 1. 问题描述 2. 解题分析 2.1 初始算法流程 2.2 优化 3. 代码及测试 4. 后记 1. 问题描述 2. 解题分析 这个题目第一感就是动态规划. 对于(m, n)形状(如下图所示, ...

  7. 程序员的算法趣题Q67: 不挨着坐是一种礼节吗?

    目录 1. 问题描述 2. 解题分析 2.1 基本思路 2.2 动态规划 2.3 算法流程 3. 代码及测试 4. 后记 1. 问题描述 注意,本问题不区分人,只考虑各个座位被占用的不同顺序的个数. ...

  8. 程序员的算法趣题Q68: 异性相邻的座位安排(1)

    目录 1. 问题描述 2. 解题分析 3. 代码及测试 4. 后记 1. 问题描述 这道题的描述应该是有问题的(不知道是原文的问题还是翻译的问题). 前面的描述中提到"前后左右的座位全是异性 ...

  9. 程序员的算法趣题Q57: 最快的联络网

    目录 1. 问题描述 2. 解题分析 2.1 学生的状态 2.2 学生状态转移 Case-T1:Do nothing, just wait Case-T2:给处于S0状态的学生打电话 Case-T3: ...

最新文章

  1. 轴对称 Navier-Stokes 方程组的点态正则性准则 I
  2. 浅聊程序化世界构建流程
  3. leetcode27 移除元素
  4. CSS快速学习5:文本溢出和XHTML元素分类
  5. mysql升级代码_phpstudy 升级mysql 及MySQL服务等问题(示例代码)
  6. ARC 101E.Ribbons on Tree(容斥 DP 树形背包)
  7. cap理论具体含义_什么是CAP定理?
  8. pdf 旋转视图,为啥不能保存?
  9. 财会法规与职业道德【8】
  10. 微服务实施笔记(一)
  11. guava之限流RateLimiter
  12. 直流无刷电机仿真分析——基于simulink官方例程BLDC Speed Control
  13. b站网页版没有html播放,网页b站能小窗口播放吗?怎么播放?最新版本bilibili小窗口播放器...
  14. 唐骏的成功——可以复制的成功
  15. STM32学习笔记——CH340一键下载电路
  16. 900页文档比对只需5分钟?鸿翼InWise文档比对,以人工智能撬动办公效率杠杆
  17. C++开发Office插件:实现Word插件
  18. 大连理工大学软件学院计算机组成原理,2018年大连理工大学软件学院810数据结构和计算机组成原理之计算机组成原理考研仿真模拟五套题...
  19. Python PyScript教程之将 Python 带入浏览器进行图像处理
  20. CSS篇之3. 如何保持浮层水平垂直居中

热门文章

  1. 坯子库无法一键安装插件没用_坯子库插件集下载-坯子插件库下载v2020.1 官方最新版-西西软件下载...
  2. 岩藻糖基化硫酸软骨素(Fucosylated Chondroitin Sulfate)
  3. 西安航空学院计算机等级成绩查询,64所院校成绩查询网址
  4. Could not resolve placeholder 'jdbc username' in string valu
  5. MiniGui 逻辑字体放大后脏点处理方法
  6. C#错误:CS0012 未能加载文件或程序集“netstandard, Version=2.0.0.0, Culture=neutral解决方案
  7. 微信实现股票查询功能
  8. 软件流程和管理(一):什么是项目
  9. Java集合题目练习
  10. Node-js-起步