1929 镜子田地(环图、位运算)

1. 问题描述:

农夫约翰在屋子外面放了一些旧镜子,他的奶牛们像往常一样调皮地偷走了它们!奶牛们将镜子放置在了一个矩形田地中,该田地可被划分为 N × M 个方格区域。在每个方格区域中,奶牛在其某对对角之间放置一个双面镜,因此,共有两种放法,一种为 / 放置(镜子连接方格左下角和右上角),另一种为 \ 放置(镜子连接方格左上角和右下角)。一天晚上,奶牛贝茜将激光发射器带到了该田地中。她站在田地外面,沿着田地的行或列水平或垂直照射光束,使光束反射一定数量的镜子。由于镜子都是沿对角线摆放,因此经反射镜反射的水平光束最终将垂直传播,反之亦然。贝茜想知道从田地之外射入的水平或垂直光束最多可以在田地中被反射多少次。给定镜子田地的布局,请帮助贝茜计算这个数字。

输入格式

第一行包含 N 和 M。接下来 N 行,每行包含 M 个 / 或 \ 字符,表示田地中镜子的具体摆放方式。

输出格式

输出田地之外的水平或垂直光束能够被反射的最大次数。如果可以无限反射,则输出 −1。

数据范围

1 ≤ N,M ≤ 1000

输入样例:

3 3
/\\
\\\
/\/

输出样例:

3
样例解释
贝茜可以从上向下沿中间列上方发射激光。共可以反射 3 次。
来源:https://www.acwing.com/problem/content/description/1931/

2. 思路分析:

分析题目可以知道如果直接做是不好做的, 我们需要将其转化为图论的问题,将每一个格子抽象成由斜线分割的两个点,如果一个点可以射入另外一个点那么这两个点之间就存在一条边,如果光从边界点射入的话那么一定会从边界点射出来的所以度数为1,如果光从内部点射入的话那么可能是存在环的,所以内部节点的度数为2,所以属于经典的环图问题(环图为只存在若干条链和若干个简单环构成的图),而题目中的光源是从边界点射入的所以一定会从边界点射出,也即是不存在简单环的,所以我们需要求解出从边界点射入的最长的链,也即需要求解出最长路:

并且可以发现从某个边界点开始射入最长路一定是确定的,也即只存在一种选择,所以我们只需要枚举出从所有的边界点射入按照上图中射入的位置走到下一个位置走出边界点那么从当前边界点位置射入的最长路径就结束了:

 

由于从某个边界点的位置开始射入的最长路是确定的,所以可以使用迭代或者dfs遍历都可以,求解出所有从边界点开始射入的位置的最长路取一个max即可。

3. 代码如下:

迭代:

from typing import List

class Solution:
    # 上右下左四个方向, 分别对应0-1-2-3这四个方向
    pos = [[-1, 0], [0, 1], [1, 0], [0, -1]]
    
    # 最长的光路是唯一确定的所以从起点到终点的位置都是确定的, (x, y)表示当前的位置, d为当前射入的光源的方向
    def solve(self, x: int, y: int, d: int, n: int, m: int, g: List[List[str]]):
        res = 0
        while True:
            # 从边界射出那么就结束了(从边界射入那么一定会从边界射出来)
            if x < 0 or x >= n or y < 0 or y >= m: break
            res += 1
            if g[x][y] == "/":
                d ^= 1
            else:
                d ^= 3
            x, y = x + self.pos[d][0], y + self.pos[d][1]
        return res

    def process(self):
        # 行数与列数
        n, m = map(int, input().split())
        g = list()
        for i in range(n):
            g.append(list(input()))
        # 模拟光线射入
        res = 0
        for i in range(n):
            # 从左边射入
            res = max(res, self.solve(i, 0, 1, n, m, g))
            # 从右边射入
            res = max(res, self.solve(i, m - 1, 3, n, m, g))
        for i in range(m):
            # 从上边射入
            res = max(res, self.solve(0, i, 2, n, m, g))
            # 从下边射入
            res = max(res, self.solve(n - 1, i, 0, n, m, g))
        return res


if __name__ == "__main__":
    print(Solution().process())

dfs(模拟迭代的过程,但是后面内存溢出了):

from typing import List
import sys

class Solution:
    pos = [[-1, 0], [0, 1], [1, 0], [0, -1]]

    def dfs(self, x: int, y: int, d: int, n: int, m: int, g: List[List[str]]):
        if x < 0 or x >= n or y < 0 or y >= m: return 0
        if g[x][y] == "/":
            d ^= 1
        else:
            d ^= 3
        return self.dfs(x + self.pos[d][0], y + self.pos[d][1], d, n, m, g) + 1

    def process(self):
        n, m = map(int, input().split())
        g = list()
        for i in range(n):
            g.append(list(input()))
        # 模拟光线射入
        res = 0
        for i in range(n):
            # 从左边射入
            res = max(res, self.dfs(i, 0, 1, n, m, g))
            # 从右边射入
            res = max(res, self.dfs(i, m - 1, 3, n, m, g))
        for i in range(m):
            # 从上边射入
            res = max(res, self.dfs(0, i, 2, n, m, g))
            # 从下边射入
            res = max(res, self.dfs(n - 1, i, 0, n, m, g))
        return res


if __name__ == "__main__":
    # 设置最大递归调用次数
    sys.setrecursionlimit(1000000)
    print(Solution().process())
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值