思路:
水解:O( n 2 n^2 n2)
考虑转化问题,判断合法性。
经过推理,我们得知每一个变量和上一个至多相差 1 。上一个变量是 2 的话,这一个只能取 3 或者 2 或者 1 。
题目可以转化为这幅图,a和b代表两个确定高度,求:
从a开始,每次可以向右上/右/右下走,不能越过紫线,求到b点的方案数
因为相邻两块积木的高度 如果相差超过 2 22 的话,无法构造。(这点应该清楚)
然后我们就可以DP 来计算方案数了。
设f[i][j] 表示 已经处理完前 i−1 个数之后,当前 第i 个数 选择了j 高度 的合法方案数。
即按照它相邻两个数相差 ≤ 1 \le 1 ≤1 的性质,我们即可推出下列柿子:(当 a i = − 1 a_i=-1 ai=−1 时)
f [ i ] [ j ] = f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j ] + f [ i − 1 ] [ j + 1 ] f[i][j]=f[i-1][j-1]+f[i-1][j]+f[i-1][j+1] f[i][j]=f[i−1][j−1]+f[i−1][j]+f[i−1][j+1]
特别地,对于越界的访问(比如 j=1 或者j=num[i−1] )就不统计越界的答案。
a i > − 1 a_i>-1 ai>−1时特判即可,不需要枚举j 。
最后这个 f 数组还要取模。
#include <cstdio>
#include <iostream>
#include <cstring>
#pragma GCC optimize(2)
#define ll long long
#define mod 1000000007using namespace std;const int N = 2e4 + 10;
int n, t, tt;
ll a[N], f[5][N];int h(int x) //求出当前点最大可能的高度
{if(x < n / 2 + 1) return x - 1;return n - x;
}int main()
{freopen("brick.in", "r", stdin);freopen("brick.out", "w", stdout);scanf("%d", &n);for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);f[0][0] = 1;for(int i = 1; i <= n; i++){t = h(i);tt = h(i - 1);if(a[i] != -1){if(a[i] <= t){f[1][a[i]] += f[0][a[i]];if(a[i]> 0) f[1][a[i]] += f[0][a[i] - 1];if(a[i] < tt) f[1][a[i]] += f[0][a[i]+1];if(!(i % 10)) f[1][a[i]] %= mod; }}else{for(int j = 0; j <= t; j++){f[1][j] += f[0][j];if(j >= 1) f[1][j] += f[0][j - 1];if(j < tt) f[1][j] += f[0][j + 1];if(!(i % 10)) f[1][j] %= mod;}}memcpy(f[0], f[1], sizeof(f[1]));memset(f[1], 0, sizeof(f[1]));}printf("%lld\n", f[0][0] % mod);return 0;
}