2025年4月22日 星期二 乙巳(蛇)年 正月廿三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 数据结构与算法

程序员的算法趣题Q61: 不交叉一笔画下去

时间:01-01来源:作者:点击数:44

1. 问题描述

根据题意要求:

  1. 各节点不能重复访问。相应地,边也不会出现重复访问的情形
  2. 由节点序列表示路径的话,正序列和逆序列表示相同的路径,不重复计算

2. 解题分析

深度优先路径遍历问题。从每一个节点出发,按照题设要求的规则进行深度优先搜索,每遍历完一次计数一次。

基本算法流程如下:

就是这么简单粗暴(简洁明了),这就是套路的力量^-^

要点说明:

  1. Nodes为全1就表示找到了一条遍历路径(完成了一次无交叉一笔画)
  2. 在每个节点处,探索上下左右是否可以画。不能出边界,外加的围栏节点初始化为“-1”,起到了保护和简化判断作用。
  3. 由于要对nodes进行赋值以表示visited状态,所以在每次递归调用前要赋值,递归调用后要恢复。另外用visited来存储已访问信息会让问题变简单吗?对于此类(网格、棋盘等)问题似乎不会,甚至更麻烦。
  4. 由于从任何一个节点出发都可以,因此外层需要对起始节点进行全扫描

以上流程没有考虑排除正序列和逆序列重复搜索的问题,因此浪费了一半的时间,所得答案要除以2才是正确答案。目前暂时没有想到如何排除正序列和逆序列重复搜索,暂时就此将就。

3. 代码及测试

  • # -*- coding: utf-8 -*-
  • """
  • Created on Thu Oct 21 07:32:03 2021
  • @author: chenxy
  • """
  • import sys
  • import time
  • import datetime
  • import math
  • # import random
  • from typing import List
  • from collections import deque
  • import itertools as it
  • import numpy as np
  • H = 5 # Height, vertical
  • W = 6 # Width, horizontal
  • # nodes initialization, with a guard band surrounding the original nodes
  • # The guard band is initialized to '-1' to simplify the judgement processing.
  • nodes0 = np.zeros((H+2, W+2))
  • nodes0[0,:] = -1
  • nodes0[H+1,:] = -1
  • nodes0[:,0] = -1
  • nodes0[:,W+1] = -1
  • count = 0
  • def dfs(h,w,nodes):
  • # print('dfs({0},{1},{2})'.format(h,w,nodes))
  • global count
  • if np.all(nodes[1:H+1,1:W+1]):
  • count = count + 1
  • return
  • if nodes[h-1,w] == 0:
  • nodes[h-1,w] = 1
  • dfs(h-1,w,nodes)
  • nodes[h-1,w] = 0
  • if nodes[h+1,w] == 0:
  • nodes[h+1,w] = 1
  • dfs(h+1,w,nodes)
  • nodes[h+1,w] = 0
  • if nodes[h,w-1] == 0:
  • nodes[h,w-1] = 1
  • dfs(h,w-1,nodes)
  • nodes[h,w-1] = 0
  • if nodes[h,w+1] == 0:
  • nodes[h,w+1] = 1
  • dfs(h,w+1,nodes)
  • nodes[h,w+1] = 0
  • tStart = time.perf_counter()
  • for h in range(1,H+1):
  • for w in range(1,W+1):
  • cur_nodes = nodes0.copy()
  • cur_nodes[h,w] = 1
  • dfs(h,w,cur_nodes)
  • cur_nodes[h,w] = 0
  • tCost = time.perf_counter() - tStart
  • print('(H,W)=({0},{1}), count = {2}, tCost = {3:6.3f}(sec)'.format(H,W,count,tCost))

运行结果:

(H,W)=(3,4), count = 124, tCost = 0.017(sec)

(H,W)=(4,5), count = 2012, tCost = 1.176(sec)

(H,W)=(5,6), count = 53992, tCost = 204.012(sec)

如上所述,将以上结果除以2才是正确答案。

4. 后记

以上只考虑了最基本解决方案,甚至连正逆序列重复搜索都没有排除,所以运行效率太低了。需要考虑优化,除了正逆序列重复搜索的排除外,还要进一步从缩小搜索范围或者剪枝(键盘敲出jiezhi变换半天看着不对劲。。。最后回过神来是jianzhi非jiezhi^-^)等角度考虑进一步提高搜索效率。

一般来说,但凡我能有一个哪怕最naive的解,我尽量避免看原书题解。要等我觉得关于这个问题已经想无可想的时候才去看原书题解。反正也不赶时间,只是利用闲暇时刻想一想(脑袋里可能并行地放了多个问题,没事的时候就翻出来想一想,所以也不浪费时间)。真正独立想出解决方案来才能有最大收获,这个也算是物尽其用吧,毕竟碰到一个好问题的机会难得。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门