浅谈点灯游戏O(n^3)算法

点灯游戏(POJ1681/NKOJ P1987)

很显然的高斯消元,将n×n的矩阵弄成一行,对其相邻格子的系数为1即可(a[i][j]表示点i号格子,j号格子是否受到影响),最后解方程即可。

NKOJ P8530 手机游戏 超级版

题意同NKOJ P1987,只不过数据范围从(1n40)到了(1n1000)

一眼不可做,直接用P1987的做法时间复杂度为O(n6),不可行

考虑到只有0/1取值,用bitset优化常数,依然不可行

继续考虑暴力做法,假设我们已经确定了第一行点了哪些格子,我们可以推出2n行每行的状态(点或不点)

即对于第i行的'#',通过i+1的点击使其变成'.',这时候对于i+1行无法再继续点击(不然维护的i行不再合法),那么对于i+1行的'#',只能通过点击i+2行消掉,以此类推,直到得到最后一行无法被消除的状态。

说的简单一点就是对于第i行,通过只点第i行的方式让i1行全部为'.',当讨论完后,1i1 全都是'.'

举个例子:

对于以下这个矩阵
.##
.#.
...

讨论点击第2行,修改第1行
点击(2,2)
..#
#.#
.#.

点击(2,3)
...
##.
.##
此时第1行已全被消除,考虑第2行

点击(3,1)
...
.#.
#.#

点击(3,2)
...
...
.#.

第2行也被消完,此时第3行还剩下一个无法被消除

显然,做完上述操作后,整个矩阵除了最后一行,其他一定是'.'

回到原题,题目是要求出给定矩阵使其全部熄灭的具体点击方案

我们发现在确定第一行状态后,我们可以推出其余行的状态(保证已经讨论过的行为熄灭)

那么从一个全熄灭状态转移到一个矩阵状态,等效于将这个矩阵全部熄灭,所以我们对于题目给出的矩阵跑一遍上述的过程,得到最后一行状态。

接着我们思考,这个东西有什么用?发现,如果一空矩阵点击了一些点后,最后一行状态(其余行全部为熄灭)等于了题目给定矩阵的最后一行状态,则这些点击的点被点后,可以将目标矩阵消完(也可以理解成
将空矩阵点成目标矩阵)

为了接下来的表述,我们把目标矩阵求的的最后一行设为A[...]

发现了以上特征后,我们开始考虑在全灭矩阵上如何使得点一些点让最后一行状态等于A[]

因为已经发现第一行的状态就可以决定最后一行,那么我们考虑枚举第一行状态,O(2n),一样炸裂(好像炸的跟裂了)

于是怎么办呢?(我白银你问我?GG)

其实很简单 (毒瘤),我们慢慢来,先讨论只点1个的情况,很简单对吧,直接点,然后往下讨论就行了,那么如果点2个呢,也很简单,可以看成先点1个,再点1个,对于重复了的部分就相当于没点,这个操作是不是有点像异或?那么对于点k个点,最后一行的状态就是把这k个点单独的状态异或起来,就完了,是不是很简单。

于是,我们可以O(n3)预处理出每个位置点击后最后一行的B[]状态,发现时间复杂度有点过分,但只有0/1取值,bitset优化即可

接着就是如何找出选出第1排的那些点,可以使得B[]异或之和等于A[]

发现单独列之间的影响是独立的,即A[j]只会受到B[][j]的影响,与其他列无关,可以看成方程组。

考虑高斯消元

把状态数组竖着摆放成为方程矩阵,xi的值表示第1行i号点是否需要点击,矩阵的值即为系数

接着解一个异或方程组就完事了

时间复杂度O(n3/64),完全跑得过

posted @   Thermalrays  阅读(364)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示