[刷题笔记] Luogu P2327 扫雷
Solution
一道非常有意思的题。
首先,有一个小性质:第二列只要第一位确定,后面的全都确定了
简单证明:
-题目已知一个数字会影响八连通,在这个题中就变成了影响左边左上左下三方向(最顶上和最底下除外),如图:
显然对于红色部分影响了其左边的黄色部分。
由此我们发现,右边的一个数字最多影响左边三个数字,若左边第一位是否有雷确定,则后面的雷都可以通过递推得到。因此,若左边的第一位是否有雷确定,答案唯一。(答案不合法除外)
因此,我们发现,无论棋盘大小,\(ans \in [0,1,2]\)(由于我打不上大括号就用[]了ww)
本题若利用这个小性质会简化很多。
我们分别令第一位有雷或无雷,分别判断答案是否合法即可。
至于如何递推的求雷个数,我们发现,一个位置雷的个数=她上一位右列雷的个数-左上-左侧
这样描述会很抽象,形式化地,若\(f_i\)表示第\(i\)行雷的个数,则满足:
\(f_i=a_{i-1}-f_{i-1}-f_{i-2}\)
本题中还有特殊情况就是当\(i=2 || n\)是否会越界,我们第一位已经确定,从第二位开始搜,最小会搜到0,\(f_0=0\)不会影响答案。故状态转移方程正确。
至此,本题的解,代码如下。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 100010
using namespace std;
int n;
int a[N];
int f[N];
bool flag1 = false,flag2 = false;
bool dp()
{
for(int i=2;i<=n;i++) f[i] = a[i-1] - f[i-1] - f[i-2]; //递推求dp值
for(int i=1;i<=n;i++) if(!(f[i] == 0 || f[i] == 1)) return 0;//如果一个位置有多个地雷不合法
if(f[n-1]+f[n] != a[n]) return 0; //边界处理不当不合法
return 1;//否则合法
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
memset(f,0,sizeof(f)); //跑两遍,记得多测清空
f[1] = 1;
flag1 = dp();
memset(f,0,sizeof(f));
f[1] = 0;
flag2 = dp();
cout<<flag1+flag2<<endl;
return 0;
}
本文作者:SXqwq,转载请注明原文链接:https://www.cnblogs.com/SXqwq/p/17599217.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!