[刷题笔记] Luogu P2327 扫雷

Problem


Solution

一道非常有意思的题。

首先,有一个小性质:第二列只要第一位确定,后面的全都确定了

简单证明:

-题目已知一个数字会影响八连通,在这个题中就变成了影响左边左上左下三方向(最顶上和最底下除外),如图:
image

显然对于红色部分影响了其左边的黄色部分。

由此我们发现,右边的一个数字最多影响左边三个数字,若左边第一位是否有雷确定,则后面的雷都可以通过递推得到。因此,若左边的第一位是否有雷确定,答案唯一。(答案不合法除外)

因此,我们发现,无论棋盘大小,\(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;
}
posted @   SXqwq  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示