题目详情可见 知道秘密的人数
比赛的时候一直在思考状态转移,自己心中想的dp[i]
含义是「第i
天知道秘密的总人数」,一直不知道怎么推出状态转移
直到看到别人的定义:dp[i]
表示第i
天新知道秘密的人数,瞬间就懂了!!!
所以第i
天新增的人数都来自(i - forget, i - delay]
区间内的人的分享,故有:
对于每次求dp[i]
都有一个区间内的累加过程,所以可以利用「前缀和」优化一波!!
xxxxxxxxxx
public int peopleAwareOfSecret(int n, int delay, int forget) {
int mod = (int) 1e9 + 7;
long[] f = new long[n + 1];
long[] preSum = new long[n + 1];
f[1] = 1;
preSum[1] = 1;
for (int i = 2; i <= n; i++) {
int l = Math.max(0, i - forget);
int r = Math.max(0, i - delay);
// 注意:对于相减的求余需要加上一个 mod,转化成正数范围内!!
f[i] = (preSum[r] - preSum[l] + mod) % mod;
preSum[i] = (preSum[i - 1] + f[i]) % mod;
}
return (int) (preSum[n] - preSum[Math.max(0, n - forget)] + mod) % mod;
}
题目详情可见 网格图中递增路径的数目
这个题目就是一个「记忆化搜索」,为了避免路径重复,可以求出以某一个点为结尾的所有路径,所以把所有点都遍历一遍即可!
private int m, n;
private int[][] grid;
private int[][] emeo;
private int mod = (int) 1e9 + 7;
private int[][] dirs = new int[][] { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
public int countPaths(int[][] grid) {
int ans = 0;
this.grid = grid;
this.m = grid.length;
this.n = grid[0].length;
emeo = new int[m][n];
// 遍历所有点
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
ans = (ans + dp(i, j)) % mod;
}
}
return ans;
}
// 以点 (i, j) 结尾的所有路径
private int dp(int i, int j) {
// 已经求过了,直接返回
if (emeo[i][j] != 0) return emeo[i][j];
long ans = 1L;
// 往四个方向递归遍历
for (int[] dir : dirs) {
int ii = i + dir[0], jj = j + dir[1];
if (ii >= 0 && ii < m && jj >= 0 && jj < n && grid[ii][jj] < grid[i][j]) {
ans = (ans + dp(ii, jj)) % mod;
}
}
// 保存结果
emeo[i][j] = (int) ans;
return emeo[i][j];
}