扫雷

题目描述

原题链接

相信大家都玩过扫雷的游戏。那是在一个\(n*m\)的矩阵里面有一些雷,要你根据一些信息找出雷来。
万圣节到了,“余”人国流行起了一种简单的扫雷游戏,这个游戏规则和扫雷一样,如果某个格子没有雷,那么它里面的数字 表示和它8连通的格子里面雷的数目。
现在棋盘是\(n×2\)的,第一列里面某些格子是雷,而第二列没有雷,如下图: 由于第一列的雷可能有多种方案满足第二列的数的限制,你的任务即根据第二列的信息确定第一列雷有多少种摆放方案。

输入描述:

第一行为N,第二行有N个数,依次为第二列的格子中的数。(1 ≤ N ≤ 10000)

输出描述:

一个数,即第一列中雷的摆放方案数。
方法一:

首先我们通过举例可以发现\(b_i\)只和\(a_{i - 1}\)\(b_{i - 1}\)\(b_{i - 2}\)是有关的。
换句话说,只要确定了这三个数那么\(b_i\)就是一个确定的值。
那么我们只需要去讨论边界的情况就能通过递推得到该方法是否符合条件。
首先\(b_1\)是显然无法通过前面的数得到,但是\(b_1\)确定了我们会发现\(b_2\)就已经确定了,所以我们只需要讨论\(b_1\)处是否有雷即可。

#include <iostream>
using namespace std;

const int N = 1e4 + 10;

int n;
int a[N], b[N];

bool check() {
	for (int i = 2; i <= n; i++) {
		b[i] = a[i - 1] - b[i - 1] - b[i - 2];
		if (b[i] < 0)return false;
	}
	if (a[n] - b[n] - b[n - 1] != 0)return false;
	return true;
}
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	int ans = 0;
	if (check())ans++;
	b[1] = 1;
	if (check())ans++;
	printf("%d\n", ans);
	return 0;
}
方法二:

考虑到本题只有两行,并且每个位置的放置显然会对后面的位置的情况造成影响。
本题可以考虑是否可以用dp的方法去处理。
可以考虑采用\(f[n][bool][bool]\)来表示当前位置\(n\)的位置上是否放地雷和下个位置是否放地雷的方法数

#include <iostream>
using namespace std;

const int N = 1e4 + 10;

int n;
int a[N];
int f[N][2][2];

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	f[0][0][0] = f[0][0][1] = 1;
	for (int i = 1; i <= n; i++) {
		if (a[i] == 0)f[i][0][0] += f[i - 1][0][0];
		if (a[i] == 1) {
			f[i][1][0] += f[i - 1][0][1];
			f[i][0][0] += f[i - 1][1][0];
			f[i][0][1] += f[i - 1][0][0];
		}
		if (a[i] == 2) {
			f[i][1][0] += f[i - 1][1][1];
			f[i][1][1] += f[i - 1][0][1];
			f[i][0][1] += f[i - 1][1][0];
		}
		if (a[i] == 3)f[i][1][1] += f[i - 1][1][1];
	}
	int ans = f[n][1][0] + f[n][0][0];
	printf("%d\n", ans);
	return 0;
}
posted @ 2021-09-06 10:53  JOKE_MAKE  阅读(89)  评论(0)    收藏  举报