程序员的算法趣题Q25: 时髦的鞋带系法
目录
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两个列表来表示当前状态,分别表示左右两列剩下的鞋带孔。由于鞋带孔不能重复穿过,基于这种表示方法,在深度搜索过程中也免去了“是否已经访问过”的判断。
除此之外,还需要:
- 指明下次应该轮到穿哪一列。activeCol: 0—left column; 1—right column
- 上一次穿过的是哪个孔: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: 时髦的鞋带系法相关推荐
- php算法求出一个数可以被分解成多少个_程序员的算法趣题
计算机的世界每天都在发生着深刻的变化.新操作系统的发布.CPU性能的提升.智能手机和平板电脑的流行.存储介质的变化.云的普及--这样的变化数不胜数. 在这样日新月异的时代中,"算法" ...
- 程序员的算法趣题 python3 - (5)
注:以下题目来自<程序员的算法趣题>– [日]增井敏克著,原书解法主要用Ruby实现,最近在学Python,随便找点东西写写当做练习,准备改成Python3实现,顺便增加一些自己的理解. ...
- LeetBook《程序员的算法趣题》Q18---水果酥饼日
<程序员的算法趣题>-(日)增井敏克 , 书中为69 道数学谜题编写了解题程序, 编程语言为:Ruby,JavaScript,C语言. Q18 水果酥饼日 日本每月的 22 日是水果酥 ...
- 程序员的算法趣题Q50: 完美洗牌
目录 1. 问题描述 2. 解题分析 2.1 思路1 2.2 思路2 3. 代码及测试 4. 后记 1. 问题描述 问题:对2n张牌洗牌,并求当1<=n<=100时,一共有多少个n可以使得 ...
- 程序员的算法趣题Q09: 落单的男女
目录 1. 问题描述 2. 解题分析 3. 代码及测试 4. 思考 1. 问题描述 人们聚集在某个活动会场上,根据到场顺序排成一排等待入场,活动的主办人员,想把人们从队列的某个位置分成两组,想要让分开 ...
- 程序员的算法趣题Q55: 平分蛋糕
目录 1. 问题描述 2. 解题分析 2.1 初始算法流程 2.2 优化 3. 代码及测试 4. 后记 1. 问题描述 2. 解题分析 这个题目第一感就是动态规划. 对于(m, n)形状(如下图所示, ...
- 程序员的算法趣题Q67: 不挨着坐是一种礼节吗?
目录 1. 问题描述 2. 解题分析 2.1 基本思路 2.2 动态规划 2.3 算法流程 3. 代码及测试 4. 后记 1. 问题描述 注意,本问题不区分人,只考虑各个座位被占用的不同顺序的个数. ...
- 程序员的算法趣题Q68: 异性相邻的座位安排(1)
目录 1. 问题描述 2. 解题分析 3. 代码及测试 4. 后记 1. 问题描述 这道题的描述应该是有问题的(不知道是原文的问题还是翻译的问题). 前面的描述中提到"前后左右的座位全是异性 ...
- 程序员的算法趣题Q57: 最快的联络网
目录 1. 问题描述 2. 解题分析 2.1 学生的状态 2.2 学生状态转移 Case-T1:Do nothing, just wait Case-T2:给处于S0状态的学生打电话 Case-T3: ...
最新文章
- 轴对称 Navier-Stokes 方程组的点态正则性准则 I
- 浅聊程序化世界构建流程
- leetcode27 移除元素
- CSS快速学习5:文本溢出和XHTML元素分类
- mysql升级代码_phpstudy 升级mysql 及MySQL服务等问题(示例代码)
- ARC 101E.Ribbons on Tree(容斥 DP 树形背包)
- cap理论具体含义_什么是CAP定理?
- pdf 旋转视图,为啥不能保存?
- 财会法规与职业道德【8】
- 微服务实施笔记(一)
- guava之限流RateLimiter
- 直流无刷电机仿真分析——基于simulink官方例程BLDC Speed Control
- b站网页版没有html播放,网页b站能小窗口播放吗?怎么播放?最新版本bilibili小窗口播放器...
- 唐骏的成功——可以复制的成功
- STM32学习笔记——CH340一键下载电路
- 900页文档比对只需5分钟?鸿翼InWise文档比对,以人工智能撬动办公效率杠杆
- C++开发Office插件:实现Word插件
- 大连理工大学软件学院计算机组成原理,2018年大连理工大学软件学院810数据结构和计算机组成原理之计算机组成原理考研仿真模拟五套题...
- Python PyScript教程之将 Python 带入浏览器进行图像处理
- CSS篇之3. 如何保持浮层水平垂直居中
热门文章
- 坯子库无法一键安装插件没用_坯子库插件集下载-坯子插件库下载v2020.1 官方最新版-西西软件下载...
- 岩藻糖基化硫酸软骨素(Fucosylated Chondroitin Sulfate)
- 西安航空学院计算机等级成绩查询,64所院校成绩查询网址
- Could not resolve placeholder 'jdbc username' in string valu
- MiniGui 逻辑字体放大后脏点处理方法
- C#错误:CS0012 未能加载文件或程序集“netstandard, Version=2.0.0.0, Culture=neutral解决方案
- 微信实现股票查询功能
- 软件流程和管理(一):什么是项目
- Java集合题目练习
- Node-js-起步