Luogu P2327 [SCOI2005]扫雷
题目传送门
思路
这道题是一道典型的 DP 题。
状态:$ f_{i, j, k} \ (0 \leq i \leq n, 0 \leq j, k \leq 1)$ 表示前 $ i + 1 $ 个格子中,第 $ i $ 个雷的情况为 $ j $ ,第 $ i + 1 $ 个雷的情况为 $ k $。
那状态转移方程呢?
原先的数组直接设成 $ a_1, a_2, ..., a_n $ 好了,
真实的雷的情况设成 $ r_1, r_2, ..., r_n $。
考虑连续三个数 $ r_{i - 1} + r_{i} + r_{i + 1} = a_{i} $ 。
当 $ a_i = 0 $ 时:
- $ r_{i - 1} = 0, r_{i} = 0, r_{i + 1} = 0 $
当 $ a_i = 1 $ 时:
- $ r_{i - 1} = 0, r_{i} = 0, r_{i + 1} = 1 $
- $ r_{i - 1} = 0, r_{i} = 1, r_{i + 1} = 0 $
- $ r_{i - 1} = 1, r_{i} = 0, r_{i + 1} = 0 $
当 $ a_i = 2 $ 时:
- $ r_{i - 1} = 0, r_{i} = 1, r_{i + 1} = 1 $
- $ r_{i - 1} = 1, r_{i} = 0, r_{i + 1} = 1 $
- $ r_{i - 1} = 1, r_{i} = 1, r_{i + 1} = 0 $
当 $ a_i = 3 $ 时:
- $ r_{i - 1} = 1, r_{i} = 1, r_{i + 1} = 1 $
这样,就可以很轻松的写出状态转移方程来了。
代码
#include <bits/stdc++.h>
using namespace std;
int f[10005][2][2];
int a[10005];
int main() {
int n;
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][0][0] += f[i - 1][1][0];
f[i][1][0] += f[i - 1][0][1];
f[i][0][1] += f[i - 1][0][0];
}
if (a[i] == 2) {
f[i][0][1] += f[i - 1][1][0];
f[i][1][0] += f[i - 1][1][1];
f[i][1][1] += f[i - 1][0][1];
}
if (a[i] == 3) {
f[i][1][1] += f[i - 1][1][1];
}
}
printf("%d", f[n][0][0] + f[n][1][0]);
return 0;
}