POJ-2870 Light Up + DFS(1级DFS+1级DFS) + Python

博客详细解析了POJ-2870 Light Up问题,通过迭代加深搜索策略,利用深度优先搜索(DFS)分为两级进行:第一级DFS寻找照亮所有障碍物的灯的方案,第二级DFS点亮剩余空白网格。文章讨论了程序设计思路,包括障碍物和空白格的表示、禁忌区域、初始顶点选择、点亮规则等,并强调了二级DFS的终止条件。作者指出代码实现复杂,建议绘制思维导图辅助理解。
摘要由CSDN通过智能技术生成

参考链接:poj2870Light Up(迭代加深搜索)_ophunter的专栏-CSDN博客

一 说明:

1.1 题目大致意思是:放置灯以照亮整个矩阵,返回所需灯的最小数量。

         给定一个矩阵如图(a)所示,其中黑色方框表示障碍物,障碍物的编号表示其周围需要的灯的数量(无编号的障碍物所需的灯数不限);除此之外,空白网格所在行列也需要有灯存在,以便于照亮该空白网格。

1.2 由于涉及寻找所有方案,所以确定本题用DFS解决。

1.2.1 将问题分解为两级DFS:

        第一级:以障碍物所需灯数为目标,寻找所有“照亮障碍物“”的安灯方案

        第二级:对第一级结果中的每一个方案进行安灯,以“照亮剩余的空白网格”。

        因此,两级方案所需的最小灯数,即为答案。

1.2.2 设计原理及注意事项

## 0 程序设计时,用不同数值的元素值反映各个信息:

        空白用-10表示,

        照亮后的行、列空白用-20表示,

        点灯位置赋值为-30。
## 1 编号为0的障碍物的4邻域为禁区,不能放置灯;
## 2 以编号最大的障碍物的邻接点为初始顶点,进行DFS搜索;
## 3 对当前障碍物的邻点放置灯时,该灯周围如果有其他障碍物,也将被照亮;
## 4 本题分为2级DFS:第一级,为障碍物点灯,寻找所有可行的方案;第二级,对各个可行方案中的空白点灯
## 4.1 在确定障碍物的点灯方案后,当对空白网格进行点灯时,灯的周围不能有障碍物,否则将使得障碍物的点灯方案有误。
## 4.1.1 如果为空白网格点灯时,其周围存在障碍物,则将该位置赋值为-100;再继续对其他空白进行点灯,直到无-10存在。最后,判断是否有-100,无-100说明点灯成功。
# # 5. 终止条件
# # 第一级dfs:所有障碍物的所需点灯均已安装完毕
# # 第二级dfs:  所有空白格均已点灯完毕
# # 6 本程序需要查询所有方案,不能使用return True or False,而是使用 return。

1.2.3 其他注意事项:

1.3 关键问题在于实施细节。

1.3.1 本人用了整整3天才做完。太菜了。代码超长。

1.3.2 我觉得应该绘制一个思维导图,用于呈现本题的程序流程,否则数天之后,本人将不记得这程序的设计逻辑。

1.4 思维导图有点可怕:

1.4.1 总图

 1.4.2 

查看PDF吧,实在是太庞大了。

POJ-2870LightUp+DFS(1级DFS+1级DFS)+Python-思维导图-互联网文档类资源-CSDN下载

1.4.3 或许应该写成Word文档。

二 代码实现

# poj2870Light Up(迭代加深搜索)
# https://blog.csdn.net/ophunter_lcm/article/details/9318079?locationNum=10&fps=1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242.1
## 结果正确,但是,太耗时了。考虑,将重复的方案去掉。
##
# 试一试吧 20211221 2108
## 0 程序设计时,用不同大小的元素值反映各个信息:空白用-10表示,照亮后的行。列空白用-20表示,点灯位置赋值为-30。
## 1 编号为0的障碍物的邻域为禁区,不能放置灯
## 2 以编号最大的障碍物的邻接点为初始顶点,进行DFS搜索
## 3 对当前障碍物的邻点放置灯时,该灯周围如果有其他障碍物,也将被照亮
## 4 本题分为2级DFS:第一级,为障碍物点灯,寻找所有可行的方案;第二级,对各个可行方案中的空白点灯
## 4.1 在确定障碍物的点灯方案后,当对空白网格进行点灯时,灯的周围不能有障碍物,否则将使得障碍物的点灯方案有误。
## 4.1.1 如果为空白网格点灯时,其周围存在障碍物,则将该位置赋值为-100;再继续对其他空白进行点灯,直到无-10存在。最后,判断是否有-100,无-100说明点灯成功。
# 5. 终止条件
# # 第一级dfs:所有障碍物的所需点灯均已安装完毕
# # 第二级dfs:所有空白格均已点灯完毕
# 6 需要查询所有方案的时候,应该不能使用return True or False,而是使用 return


# # 一些约束条件:
# 1. 障碍旁边的灯的个数与其编号相同
# 2. 同一行或列最多是有一个灯,即已经被点亮的地方,不能再放置灯
# 3. 所有的空白均被照亮
# 4. 灯可以照亮未被障碍堵塞的整行或整列



# 8 迭代返回和终止的条件是什么?
import collections
import math
#1.1 数据转化
# 1.1 注意障碍数是否为0
# 1.2 注意将障碍的横纵坐标均减去1,再进行赋值
def sub2Dict(RR,CC,B_num,B_data):
    ##
    subData = [[-10]*CC for i in range(RR)] # # # 空白用-10表示
    ## 1 将障碍添加到数据矩阵中,同时获得障碍矩阵
    Barrier_dict = collections.defaultdict(list)
    if B_num > 0:
        for bi in range(B_num):
            rr,cc,kk = map(int,B_data[bi].strip().split(' '))
            subData[rr-1][cc-1] = kk # 横纵坐标减去1,使之从下表0开始编号
            # Barrier_dict[kk] = [str(rr-1)+';'+str(cc-1),rr-1,cc-1,kk] # 坐标索引,横坐标、纵坐标、所需的灯数 # 隐患:只保存一次-1障碍,本字典记录的障碍数小于实际数
            Barrier_dict[str(rr - 1) + ';' + str(cc - 1)] = [str(rr - 1) + ';' + str(cc - 1), rr - 1, cc - 1, kk]
    ## 将数据转化为dict
    Data_dict = collections.defaultdict(list)
    Stop_dict = collections.defaultdict(list) # 需要提前设置该空值
    for ri in range(RR):
        for ci in range(CC):
            # 0 获得元素坐标
            Data_dict[str(ri)+';'+str(ci)] = subData[ri][ci]
            ## 1 查找编号为0的元素的邻域,它的空位置领域,不能放置灯,即得到禁区的坐标
            if subData[ri][ci] == 0:
                Stop_dict = ForNeibs(ri, ci, RR, CC, subData)
    return subData,Data_dict,Stop_dict,Barrier_dict
## 查找节点的可用邻域
def ForNeibs(ri,ci,RR,CC,subData):
    Neib_Dict = collections.defaultdict(list)
    ## 查找当前节点的有效的4邻域,且只有其为空格时才属于有效的潜在置灯位置
    ## 是否加入,不属于禁区呢?
    if 0 <= ri + 1 <= RR - 1 and subData[ri + 1][ci] == -10:
        node1 = str(ri + 1) + ';' + str(ci)
        Neib_Dict[node1] = [subData[ri + 1][ci]]
    if 0 <= ri - 1 <= RR - 1 and subData[ri - 1][ci] == -10:
        node1 = str(ri - 1) + ';' + str(ci)
        Neib_Dict[node1] = [subData[ri - 1][ci]]
    if 0 <= ci + 1 <= CC - 1 and subData[ri][ci + 1] == -10:
        node1 = str(ri) + ';' + str(ci + 1)
        Neib_Dict[node1] = [subDat
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值