P159 摆动序列

这道题难度较大,使用了动态规划和前缀和

我们先来说什么是动态规划

动态规划 = 把大问题拆成小问题 + 记住小问题的答案 + 用小答案拼出大答案

举一个简单的例子爬楼梯,一次只能爬2或3层

当你爬楼梯的时候,你站在第一层,当你要迈向第二层楼梯的时候,你只有1种走法

迈向第三层的时候,你可以选择先走到第二层再走到第三层,也可以选择直接走到第三层,这就有2种走法

迈向第四层的时候,你可以选择 到第二层到第三层到第四层 ,也可以选择 到第二层到第四层 ,也可以选择到第三层到第四层,这就有3种走法 也就是第二层走法+第三层走法 1+2=3

迈向第五层时:第三层的走法+第四层的走法 也就是2+3 = 5层

所以到第N层: 第N-2层的走法+第N-1层的走法

我们先看代码:

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Main {
    static final int MOD = 10000;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();

        int[][] up = new int[m + 1][n + 1];
        int[][] down = new int[m + 1][n + 1];

        for (int j = 1; j <= n; j++) {
            up[1][j] = 1;
        }

        for (int i = 2; i <= m; i++) {
            if (i % 2 == 0) {
                // 后缀和
                int[] suffix = new int[n + 2];
                for (int k = n; k >= 1; k--) {
                    suffix[k] = (suffix[k + 1] + up[i - 1][k]) % MOD;
                }
                for (int j = 1; j <= n; j++) {
                    down[i][j] = suffix[j + 1];
                }
            } else {
                // 前缀和
                int[] prefix = new int[n + 2];
                for (int k = 1; k <= n; k++) {
                    prefix[k] = (prefix[k - 1] + down[i - 1][k]) % MOD;
                }
                for (int j = 1; j <= n; j++) {
                    up[i][j] = prefix[j - 1];
                }
            }
        }

        int ans = 0;
        if (m % 2 == 1) {
            for (int j = 1; j <= n; j++) ans = (ans + up[m][j]) % MOD;
        } else {
            for (int j = 1; j <= n; j++) ans = (ans + down[m][j]) % MOD;
        }
        System.out.println(ans);
    }
}

我们分部分解释:

这一段代码是算法题(尤其是动态规划、数论类题目)中非常经典的防溢出 + 取模写法。

final表示这是一个常量,防止后面把MOD给改了

static表示MOD是全局通用的一个常量

取模是为了防止整数溢出

up是奇数步,down是偶数步

1代表第一步,j表示格子编号

因为第一步可以站在任意格子上,所以无论走到哪个格子上都只有1种走法

这是动态规划和后缀和的核心代码

定义后缀和suffix的意义是为了避免第三次for循环,为了避免超时。

举个例子,当k=1的时候,就意味着你要跳到编号是1的格子上,因为是偶数,所以你只能从上往下跳,也就是从2到n的格子上往下跳,所以 down[i][1] = 上一步所有 k>1 格子的走法之和。这时候后缀和就帮你把这些从后往前数的数加起来

suffix[k]从 k 格到 n 格的所有走法之和

  • suffix[n] = up[i-1][n](只有自己)
  • suffix[n-1] = suffix[n] + up[i-1][n-1](n-1 + n)
  • suffix[k] = suffix[k+1] + up[i-1][k](k 到 n 的总和)

最后取模防止整数溢出

最后一步是将后缀和赋值给down

  • down[i][j]:第 i 步走到 j 格,且下一步要向下的走法数
  • suffix[j+1]从 j+1 格到 n 格的所有走法之和 → 正好是「所有能从 j 上面跳下来的走法」

所以简单来说就是

偶数步向下走时,用后缀和快速算出「所有比 j 大的格子的走法之和」,赋值给 down[i][j],既高效又避免重复计算。

这一部分和上一部分类似,不同的地方是前缀和

因为奇数步的上一步是偶数步,所以计算 这一步的前缀和 只需要计算 上一步的前缀和 再加上 上一步 也就是 偶数步 的走法就可以

最后一步就是把前缀和赋值给up

向上走,只能走比j小的数,所以前缀和是j-1不是j

这一步是输出答案

奇数步的时候就用up up[m][j]:第 m 步(奇数步)走到 j 格的方案数

偶数步就用down down[m][j]:第 m 步(偶数步)走到 j 格的方案数

题目要的是所有可能的终点 ,不管最后站在哪个格子,只要走了 m 步都算,所以要把 j=1j=n 的所有方案数全部加起来

相关推荐
wno70419 分钟前
Spring Security权限控制
java·python·spring
杨运交1 小时前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring
Geek-Chow1 小时前
CountDownLatch in Java
java
漂流瓶jz1 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva
SL_staff2 小时前
从RBAC到场景化授权:《无忧·企业文档》三级权限模型的技术实践解析
java·开源·产品
传奇开心果编程2 小时前
【springboot基础语法学与练】第 1 课:从零开始
java·spring boot·后端·学习
SL_staff2 小时前
财务系统慎用低代码?从数据模型闭环看合规落地的技术实践
java·低代码·全栈
Bruce_Liuxiaowei2 小时前
Python 实例赋值遮蔽类属性:一个从不报错的静默陷阱
开发语言·python·语法糖
叠层归一研究院3 小时前
基于极限自指的叠层归一宇宙结构理论——无元外部封闭系统的内生区分模型
人工智能·经验分享·算法·agi
滕州市燕猫虎计算机科技工作室个体工商户3 小时前
IDEA:Command line is too long
java·ide·intellij-idea