题目详情可见 知道秘密的人数
比赛的时候一直在思考状态转移,自己心中想的dp[i]含义是「第i天知道秘密的总人数」,一直不知道怎么推出状态转移
直到看到别人的定义:dp[i]表示第i天新知道秘密的人数,瞬间就懂了!!!
所以第i天新增的人数都来自(i - forget, i - delay]区间内的人的分享,故有:
对于每次求dp[i]都有一个区间内的累加过程,所以可以利用「前缀和」优化一波!!
xxxxxxxxxxpublic 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];}