P2437 蜜蜂路线 - 洛谷 | 计算机科学教育新生态

DP五部曲👇🏻对于此题
确定 dp[i] 含义ifib() 数值为 dp[i]
递推公式dpn=dpn1+dpn2dp_n = dp_{n-1} + dp_{n-2}
dp[] 如何初始化dp0=1dp1=1dp_0 = 1 \quad dp_1 = 1
遍历顺序从前向后
打印 dp[]DEBUG

Template Code (对于 DP 来说,模版没什么用,重点还是看上面的五部曲)

C++
对于通过此题,还需要高精度,但本文讲动态规划,所以不涉及那部分内容,通过两个测试点即可

//build: g++ P1605.cpp -std=c++20 -o P1605
#include <iostream>
#include <vector>

void solve();

int main(){
    solve();

    return 0;
}



void solve() {
    int m, n;
    std::cin >> m >> n;
    n = n - m;
    // std::vector<BigInt> dp(n + 1);
    std::vector<BigInt> dp(3);
    dp[0] = 1; dp[1] = 1; // 由题目信息定义
    
    for (int i = 2; i <= n; ++i) { // 这里 fib 从 0 开始,所以求到 n --- f(0) = 1, f(1) = 1
        // 开 n 长 vector 的做法
        // dp[i] = dp[i - 1] + dp[i - 2];
        // dp[i - 2] = dp[i - 1];
        // dp[i - 1] = dp[i];
        // 三个变量的做法
        dp[2] = dp[1] + dp[0];
        dp[0] = dp[1];
        dp[1] = dp[2];
    }
    std::cout << dp[2] << std::endl;
}

DP思考问题的方式比较反直觉,我一开始学的时候感觉特别不适应,分享一下我的思考过程:

  1. 找最优子结构:对一个问题,先假设任意子问题已经解好(wishful thinking),然后看看能不能从子问题得出大问题。
    比如Fib(n) 是由子问题Fib(n - 1) + Fib(n - 2)得来的。
  2. DP or 分治: 观察子问题的结构,看看子问题的集合是否是完全不相交,如果有相交,初步判断可以用DP优化避免重复计算,如果没有相交,则是简单分治(dp就是分治,只是多了一步用自底向上的方法去优化分治后问题的重复计算)。
    比如Fib(n)会计算Fib(n - 2), 他的子问题Fib(n - 1)又会计算一遍Fib(n - 2),背后是一颗带有重复节点的树。而分治问题比如归并排序,它的子问题是sort(a, b) = merge(sort(a, mid), sort(mid + 1, b)),子问题完全不相交,直接用递归就可以了。
  3. 先用递归的方式用人习惯的思考模式(自顶向下)表达出来(熟练跳过)
    fib(n) = fib(n - 1) + fib(n - 2) // 结束状态这里省略
  4. 确定状态存储的结构,然后自底向上把DP写出来。

© 2018-2025 0xFFFF