PTA动态规划实战:从最大子段和到背包问题,5个经典题目详解(附Java代码)

PTA动态规划实战:从最大子段和到背包问题,5个经典题目详解(附Java代码)

如果你正在准备算法竞赛或者技术面试,动态规划(Dynamic Programming, DP)这道坎儿,大概率是绕不过去的。我刚开始接触DP的时候,感觉它就像个黑盒子,状态转移方程看得人云里雾里,明明每个字都认识,连起来却不知道在说什么。直到后来在PTA(Programming Teaching Assistant)平台上刷了几十道题,从最简单的最大子段和一路磕磕绊绊到复杂的区间DP,才慢慢摸到了一点门道。我发现,与其去死记硬背那些晦涩的理论,不如直接动手,把几个最经典、最常考的题目吃透,理解其背后的“状态”是如何定义和转移的。这篇文章,我就想和你分享这趟实战之旅中的五个关键“驿站”,它们覆盖了线性DP、背包、序列处理、区间规划等核心场景。我会用详细的Java代码和一步步的推理,帮你把PTA上这些经典题目的“筋骨”拆解清楚,让你下次再遇到DP问题时,心里能有个清晰的解题框架,而不是一片空白。

1. 最大子段和:动态规划的“入门仪式”

最大子段和问题,常被称作动态规划最好的“入门仪式”。它的描述极其简单:给定一个可能包含负数的整数序列,找出其中连续子序列的最大和。如果所有整数都是负数,那么最大和定义为0。题目要求时间复杂度为O(n),这直接排除了暴力枚举所有子序列的O(n²)或O(n³)解法,暗示我们必须用更聪明的方式。

为什么它能用动态规划解决? 关键在于我们如何定义“状态”。一个最自然的想法是:dp[i] 表示“以第 i 个元素结尾的”所有连续子序列中,和最大的那个值。注意这个定义,它固定了子序列的终点,这样我们就能利用之前计算的结果。

提示:在动态规划中,对状态的定义往往决定了递推关系的难易。将状态定义为“以i结尾”而非“从i到j”,是简化问题的关键一步。

那么,dp[i] 怎么从之前的状态推导出来呢?对于以 arr[i] 结尾的子序列,只有两种可能:

  1. 它单独构成一个子序列,和就是 arr[i]
  2. 它接在以 arr[i-1] 结尾的最大和子序列后面,和是 dp[i-1] + arr[i]

我们的目标是和最大,所以取两者中的较大值:dp[i] = max(arr[i], dp[i-1] + arr[i])

这个递推关系就是整个算法的核心。我们只需要遍历一次数组,不断更新 dp[i],同时用一个变量记录所有 dp[i] 中的最大值,即为最终答案。

import java.util.Scanner;

public class MaxSubArraySum {
    public static int maxSubSum(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int currentMax = arr[0]; // 相当于dp[i-1],初始为第一个元素
        int globalMax = arr[0];  // 记录全局最大值

        for (int i = 1; i < arr.length; i++) {
            // 状态转移:currentMax = max(arr[i], currentMax + arr[i])
            currentMax = Math.max(arr[i], currentMax + arr[i]);
            // 更新全局最大值
            globalMax = Math.max(globalMax, currentMax);
        }
        // 根据题意,如果全局最大值小于0,则返回0
        return globalMax > 0 ? globalMax : 0;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = scanner.nextInt();
        }
        System.out.println(maxSubSum(arr));
        scanner.close();
    }
}

这段代码是上述思路的空间优化版本。我们注意到,在计算 dp[i] 时,只依赖于 dp[i-1],因此不需要保存整个 dp 数组,用一个变量 currentMax 滚动更新即可。空间复杂度从O(n)降到了O(1)。PTA上的样例输入 -2 11 -4 13 -5 -2,计算过程如下表所示:

i arr[i] currentMax (更新前) currentMax (更新后) globalMax
0 -2 -
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值