[LeetCode解题报告] LCP 48. 无限棋局

  • 一、 题目
    • 1. 题目描述
    • 2. 原题链接
  • 二、 解题报告
    • 1. 思路分析
    • 2. 复杂度分析
    • 3. 代码实现
  • 三、 本题小结

一、 题目

1. 题目描述

2. 原题链接

链接: LCP 48. 无限棋局

二、 解题报告

1. 思路分析

大模拟
  • 将思路转化为寻找胜利点,也就是下在这里能赢(创造5连)。
  • 如何找呢,显然如果一步就能获胜的话(胜利点1个),这时棋盘上现有的黑旗必占据胜利线段的4个位置,至少有一个端点被占,因此枚举现有黑棋作为一个端点的情况,往一个方向枚举线段情况即可。
  • 这个线段的5个位置必须有4个黑棋和1个空位,这个空位就是胜利点
  • 这个动作的复杂度是n84,n个黑棋,8个方向,4个需要枚举的位置(因为线段长度是5,除了端点自己)

  • 现在可以开始分类讨论了:
    1. 如果起始状态,黑棋存在胜利点,直接黑赢。
    1. 起始黑赢不了,观察白的胜利点:
    1. 如果白存在2个以上胜利点,则第一步黑防不住,白赢。
    1. 如果白存在1个胜利点,则黑第一步必下这里。下完观察黑现在的胜利点。注意此时白已经赢不了了。
    1. 如果此时黑有2个以上胜利点,白防不住,黑赢。
    1. 如果此时黑有1个胜利点,白下这里,无胜者,return 'None'
    1. 如果白不存在胜利点,白反正赢不了了,黑要考虑策略,使得自己一步下完存在多个胜利点使白防不住。
    1. 如果存在任意一个点,黑下完后存在两个胜利点,则黑赢。
    1. 如果任意一个点都不能满足上述情况,则无胜者,return 'None'

  • 在2.3步骤中,我尝试了直接用了一开始写的找胜利点方法:需要枚举模拟所有黑可能下的第一步位置,然后寻找胜利点。
  • 这种情况需要枚举模拟n个点的周围距离2以内所有空位,因此复杂度n×24 × n×8×4,n=1000,24w×32w,TLE。
  • 然后剪枝了一下寻找胜利点的方法,如果超过2个,直接提前返回。这时可以6000+ms低空飘过。
  • 于是从题解找了更佳的方法:多写一个寻找胜利点的方法,对于这条size5的线段,寻找3个黑棋2个空位的情况,我们称这俩空位是兄弟。
  • 显然,只要第一步下一个点,第二下兄弟即可获胜。
  • 那么如果存在一个空位存在两个以上兄弟,只需要第一步下这个空位,白旗防不住所有兄弟,则黑赢。
  • 这样就不用疯狂枚举第一步的落脚点了。时间复杂度优化到n×4×8 ×2。
  • 最后的×2怎么来的呢,因为这时就不仅能枚举每个点作端点了,还需要枚举它作为第二个点才会不漏。
  • 考虑 .xxx.这种情况(.代表空位,x代表黑棋),显然没有任何一个现存黑棋是最终胜利线段的端点;除了这种情况以外,都必存在一个现存黑棋作为胜利线段的端点;因此我们补齐这种情况即可,我这里枚举黑棋作为第二个点,计数时向前3步+向后1步即可。
  • 最终388ms。

2. 复杂度分析

n48

3. 代码实现

DIRS = [(0,1),(0,-1),(1,0),(-1,0),(1,1),(1,-1),(-1,1),(-1,-1)]
class Solution:def gobang(self, pieces: List[List[int]]) -> str:ws = set()bs = set()for x,y,t in pieces:if t == 0:bs.add((x,y))else:ws.add((x,y))def find_win_point(bs,ws,target=None):# 如果没有target,复杂度是n*4*8,n是黑旗个数,也就是32000左右# 如果有target,n = 4+1+4=9。复杂度是9*4*8≈300# 寻找bs的制胜点,遍历每个点以它作为端点,向每个方向延伸4个点,这四个点必须满足3个同色,一个空位,才是制胜点。# 这里注意,如果只允许加一个点,那么现存的点一定有一个是获胜线段的端点;如果允许加两个,就不一定了。# 优化:当target不空时,只需要遍历能够到它的点即可,即target必须在获胜线段中。ans = set()if target:i,j = targetbs = [(x,y) for x in range(i-4,i+5) for y in range(j-4,j+5) if (x,y) in bs]for x,y in bs:  # 枚举每个点作为获胜线段的端点,向8个方向尝试4个位置。for dx,dy in DIRS:steady = 1  # 本线段上黑旗计数epty = None   # 空位是谁for i in range(1,5):a,b = x+dx*i,y+dy*i if (a,b) in ws:break  # 如果是白旗,不满足                       if (a,b) in bs:steady += 1;continue  # 如果是黑旗,计数加一,直接去下一个位置                                  if epty:break  # 到这里一定是空位,但如果是第二个空位,也不满足,我们需要这条线上只有1个空位。epty = (a,b)if steady == 4 and epty:  # 一定要有4个黑旗和1个空位才可以                      ans.add(epty)if len(ans)>=2:return ans  # 只用到最多两个制胜点进行判断,提前返回return ans# 如果黑旗有制胜点,直接返回黑if find_win_point(bs,ws):return 'Black'             w_ans = find_win_point(ws,bs)# 如果白棋有两个以上制胜点,返回白if len(w_ans) >1:return 'White'if len(w_ans) == 1:# 如果白旗有一个制胜点,黑旗必须下在这;白旗赢不了了。bs.add(w_ans.pop())# 寻找黑旗制胜点b_ans = find_win_point(bs,ws)# 这时如果黑旗有两个制胜点,返回黑if len(b_ans) > 1:return 'Black'# 如果黑旗有一个制胜点或没有,白旗下在这,黑旗下一步赢不了,返回noneelse:return 'None'# # 这段 6032 ms,因为大量枚举+试探寻找# # 如果黑、白旗都没有制胜点,那么黑旗需要一步下完出现两个制胜点才能赢,否则就是None# # 枚举黑旗要下的点,看看是否存在满足条件的点# # 这一步一定是下在现有黑旗的附近距离不超过2的,否则没有意义,因为之后只能填一次了,要利用这步的黑旗才行# # 因此枚举次数是n*24 (5*5的格子除了自己都要尝试),n是黑旗个数,当然存在访问过的点vis不需要重复尝试。# # 24000*32000会炸,因此在find中加入了target,我们认为只需要围绕着现在尝试的点找连线即可,这样优化成32000*300,勉强能过# vis = bs|ws# for x,y in bs:#     for dx,dy in DIRS:#         for i in range(1,3):#             a,b = x+dx*i,y+dy*i #             if (a,b) not in vis:#                 bs.add((a,b))#                 vis.add((a,b))#                 if len(find_win_point(bs,ws,(a,b))) >= 2:return 'Black'#                 bs.remove((a,b))# 388 ms# 直接寻找可能获胜的线段,这条线段应该满足有三个黑和两个空位。我们管这俩空位叫兄弟。# 这两个空位的意义是:如果一个位置填了黑,只需要再填另一个就能赢。# 那么当一个空位如果有多个兄弟,那只需要第一步下这个空位就能赢,因为白旗无法全部防范。# 注意这里,黑旗不能只作为端点枚举,还要枚举作为第二个点;这是因为如果出现 X...X 这种形式,现有的黑旗并不在获胜线段的端点上;这时一定可以枚举作为第二个点,同时除此以外的情况,必有一个点在端点上,因此不会漏。live_pair = defaultdict(set)for x,y in bs:for dx,dy in DIRS:# 枚举作为端点empty = []cnt = 1for i in range(1,5):a,b = x+dx*i,y+dy*i if (a,b) in bs: cnt += 1;continueif (a,b) in ws: break if len(empty) == 2:break empty.append((a,b))if cnt == 3 and len(empty) == 2:a,b = emptyif len(live_pair[a]) == 2 or len(live_pair[b]) == 2:return 'Black'live_pair[a].add(b)live_pair[b].add(a)# 以下枚举作为第二个点empty = []cnt = 1for i in range(1,4):  # 向前方计数3个位置a,b = x+dx*i,y+dy*i if (a,b) in bs: cnt += 1;continueif (a,b) in ws: break if len(empty) == 2:break empty.append((a,b))for i in range(1,2):  # 向后计数1个位置a,b = x-dx*i,y-dy*i if (a,b) in bs: cnt += 1;continueif (a,b) in ws: break if len(empty) == 2:break empty.append((a,b))if cnt == 3 and len(empty) == 2:a,b = emptylive_pair[a].add(b)live_pair[b].add(a)if len(live_pair[a]) == 2 or len(live_pair[b]) == 2:return 'Black'return 'None'

三、 本题小结

  1. 大模拟需要细节,但最重要的是不要怕。
  2. 这时将题目转化的关键点想清楚最重要,最好提前写好分支,然后代码纯翻译。

[LeetCode解题报告] LCP 48. 无限棋局相关推荐

  1. [LeetCode解题报告] LCP 49. 环形闯关游戏

    [LeetCode解题报告] LCP 49. 环形闯关游戏 一. 题目 1. 题目描述 2. 原题链接 二. 解题报告 1. 思路分析 2. 复杂度分析 3. 代码实现 三. 本题小结 四. 参考链接 ...

  2. LeetCode解题报告汇总

    LeetCode解题报告: [LeetCode]1.Two Sum - Yoona - 博客频道 - CSDN.NET [LeetCode]2.Add Two Numbers - Yoona - 博客 ...

  3. [LeetCode解题报告] 365. 水壶问题

    [LeetCode解题报告] 365. 水壶问题 一. 题目 1. 题目描述 2. 原题链接 二. 解题报告 1. 思路分析 2. 复杂度分析 3. 代码实现 三. 本题小结 一. 题目 1. 题目描 ...

  4. [LeetCode解题报告] 741. 摘樱桃

    [LeetCode解题报告] 741. 摘樱桃 一. 题目 1. 题目描述 2. 原题链接 二. 解题报告 1. 思路分析 2. 复杂度分析 3. 代码实现 三. 本题小结 一. 题目 1. 题目描述 ...

  5. LeetCode 解题报告索引

    最近在准备找工作的算法题,刷刷LeetCode,以下是我的解题报告索引,每一题几乎都有详细的说明,供各位码农参考.根据我自己做的进度持续更新中......                        ...

  6. leetcode解题报告:188 Best Time to Buy and Sell Stock IV

    问题: 给定一个列表,第i个元素代表股票第i天的价值,最多只允许买入卖出k次,求最大收益 思路:动态规划 输入为列表p1p2...pm 代码:Python 转载于:https://blog.51cto ...

  7. leetcode解题报告:198 House Robber

    问题描述: 一个小偷去一个街区偷东西,求偷得价值最大,唯一限制就是不能偷连续的两家,因为这样会触发警报. 建模: 给定一个列表,里面存着每家可以偷的价值,输出最大偷到的价值. 思路:动态规划 如果输入 ...

  8. leetcode解题报告:Interleaving String

    问题描述: 给定两个字符串s1, s2,判断字符串s3是否是由s1.s2交错构成. 例子: s1 = "aabcc", s2 = "dbbca", When s ...

  9. 【LeetCode解题报告】《算法基础012_因子和》- Java

    目录 一.1390.四因数 1.题目 2.分析 3.代码 一.1390.四因数 1.题目 1390.四因数 给你一个整数数组 nums,请你返回该数组中恰有四个因数的这些整数的各因数之和. 如果数组中 ...

最新文章

  1. JSP实现银柜台业务绩效考核系统
  2. FW/IDS/IPS/WAF等安全设备部署方式及优缺点
  3. wxWidgets:wxAuiNotebook类用法
  4. 神奇的折纸艺术!无限翻转完全停不下来
  5. 搜索服务Elasticsearch与Solr比较
  6. 使用tcl 创建vivado工程
  7. 【EasyNetQ】- 发布
  8. Egret入门学习日记 --- 问题汇总
  9. 集体智慧编程 - 读书笔记
  10. 数论中的偶数阶Abel群的阶
  11. eXtremeComponents介绍
  12. 面向对象:寻寻觅觅,诚邀你一起来解开这道迷题
  13. 互联网、云计算之用户服务
  14. OllyDbg逆向破解入门攻略?拿来吧你!
  15. 阿里云对象存储OSS(Object Storage Service)
  16. 函数极限:函数在一点处的极限
  17. 帮我看看怎么回事的错误
  18. 在Windows中开启Wifi热点共享,无需第三方工具
  19. matplotlib (一) 图的基本操作
  20. 计算机应用讲课大赛,xx杯说课大赛计算机应用基础类一等奖作品: PPT写字动画的制作现场说课课件...

热门文章

  1. 如何区分网线是几类的_怎么区分买的网线是几类网线?
  2. 女孩子付钱用计算机,“让女生付钱太没面子了,你转账给我吧。”
  3. 单片机补充案例--两只老虎
  4. Centos | 一招解决所有 ImportError: xxx: cannot open shared object file
  5. 神兵利器 nth_element
  6. Launch Failed,Binary not found
  7. 做为一个玩家如何架设传奇
  8. mt4 显示服务器时间,mt4服务器时间设置
  9. 《精彩网址大全——生活资讯文体娱乐卷》前言
  10. MATLAB Support Package for USB Webcams 的使用方法(MATLAB调用摄像头后的使用方法)