题目分析
题目将扫雷游戏简化,改为只有一列有雷的版本
a[i] 表示第 i 格的数字
f[i][j][k]表示当前做到第 i 个位置,第 i 个位置的状态为 j(1表示无雷,2雷),第 i+1 位置状态为 k
由雷的个数写出状态转移方程:
f[i][1][1] :此时两个位置上都没有雷,此时 a[i] 个雷全部来自第 i-1格
可以算出第 i-1 格的雷数:a[i] - 0 - 0 = a[i]
此时
f[i][1][1] = f[i-1][a[i]+1][1]; //因为这里 1 表示 0,2 表示 1,所以a[i]要加1
同理可得另外三条语句:
f[i][1][2] = f[i-1][a[i]][1]; f[i][2][1] = f[i-1][a[i]][2]; f[i][2][2] = f[i-1][a[i]-1][2];
最后输出答案:
printf ("%d", f[n][1][1] + f[n][2][1]); //因为第 i+1 个格子肯定没有雷,所以不能加上f[n][1][2] + f[n][2][2](可以试试,WA一个点)
空间优化
这样已经可以 AC了,但还可以再优化一下
不难发现,转移时 f[i][j][k] 都是由分f[i-1][x][y] 转移而来,因此可以用滚动数组优化
用 & 运算来实现优化,优化后 dp 代码:
for (int i = 1; i <= n; i++) { f[i][1][1] = f[i-1][a[i]+1][1]; f[i][1][2] = f[i-1][a[i]][1]; f[i][2][1] = f[i-1][a[i]][2]; f[i][2][2] = f[i-1][a[i]-1][2]; }
代码
未优化
#include <bits/stdc++.h> using namespace std; int n, a[10038], f[10038][5][5], ans; int main() { scanf ("%d", &n); for (int i = 1; i <= n; i++) scanf ("%d", a + i); f[0][1][1] = f[0][1][2] = 1; for (int i = 1; i <= n; i++) { f[i][1][1] = f[i-1][a[i]+1][1]; f[i][1][2] = f[i-1][a[i]][1]; f[i][2][1] = f[i-1][a[i]][2]; f[i][2][2] = f[i-1][a[i]-1][2]; } printf ("%d", f[n][1][1] + f[n][2][1]); return 0; }
优化版
#include <bits/stdc++.h> using namespace std; int n, a, f[2][5][5], ans; int main() { scanf ("%d", &n); f[0][1][1] = f[0][1][2] = 1; for (int i = 1; i <= n; i++) { scanf ("%d", &a); f[i&1][1][1] = f[(i+1)&1][a+1][1]; f[i&1][1][2] = f[(i+1)&1][a][1]; f[i&1][2][1] = f[(i+1)&1][a][2]; f[i&1][2][2] = f[(i+1)&1][a-1][2]; } printf ("%d", f[n&1][1][1] + f[n&1][2][1]); return 0; }