HDOJ 1003 最大子序列和

这道关于DP的经典题终于花时间弄明白了。一定要记录一下啊。题目链接。思路对这个题目,其实最重要的是思路。参考了此博客上面的思路,在这里对DP求解的思路做一些整理。设数组$arr$的长度为$n$,下标从$0$到$n-1$,则最后一个元素表示为$arr[n-1]$。对连续和最大的子数组(后称"

这道关于DP的经典题终于花时间弄明白了。一定要记录一下啊。题目链接

思路

对这个题目,其实最重要的是思路。参考了此博客上面的思路,在这里对DP求解的思路做一些整理。


设数组$arr$的长度为$n$,下标从$0$到$n-1$,则最后一个元素表示为$arr[n-1]$。对连续和最大的子数组(后称"最大子数组"),最后一个元素$arr[n-1]$有如下三种情况:

  • $arr[n-1]$单独构成最大子数组
  • 最大子数组以$arr[n-1]$结尾
  • 最大子数组跟$arr[n-1]$没关系,最大子数组在$arr[0-n-2]$范围内,转为考虑元素$arr[n-2]$

从上面我们可以看出,问题分解成了三个子问题,最大子数组就是这三个子问题的最大值,现假设:

  1. 以$arr[n-1]$为结尾的最大子数组和为$End[n-1]$
  2. 在$[0,n-1]$范围内的最大子数组和为$All[n-1]$

如果最大子数组跟最后一个元素无关,即最大和为$All[n-2]$(存在范围为$[0, n-2]$),则解 $All[n-1]$ 为下述三种情况的最大值,即

  • $arr[n-1]$
  • $End[n-1]$
  • $All[n-2]$

从后向前考虑,初始化的情况分别为$arr[0]$,以$arr[0]$结尾,即$End[0] = arr[0]$,最大和范围在$[0,0]$之内,即$All[0]=arr[0]$。根据上面分析,$All[i]$ 的值应该取下述三种情况的最大值:

  • $arr[i]$
  • $End[i-1]+arr[i]$
  • $All[i-1]$

伪代码表示为:

/* DP base version*/
#define max(a,b) ( a > b ? a : b)
 
int Maxsum_dp(int * arr, int size)
{
    int End[30] = {-INF};
    int All[30] = {-INF};
    End[0] = All[0] = arr[0];
 
    for(int i = 1; i < size; ++i)
    {
        End[i] = max(End[i-1]+arr[i],arr[i]);
        All[i] = max(End[i],All[i-1]);
    }
    return All[size-1];
}

仔细看上面DP方案的代码,End[i] = max{arr[i],End[i-1]+arr[i]},如果$End[i-1]<0$,那么$End[i]=arr[i]$,什么意思?

$End[i]$表示以i元素为结尾的子数组和,如果某一位置使得它小于0了,那么就自当前的$arr[i]$从新开始,且$End[i]$最初是从$arr[0]$开始累加的。

所以这可以启示我们:我们只需从头遍历数组元素,并累加求和,如果和小于0了就自当前元素从新开始,否则就一直累加,取其中的最大值便求得解。

为什么当sum<0时可以舍弃序列并重新扫描呢?

证明如下:

设$i$表示子序列的起始下标,$j$ 表示子序列的终止下标。

当我们得到一个子序列,如果子序列的第一个数是非正数,那么可以舍去,即$i = i + 1$

当一个子序列的前$n$个元素和为非正数时,是否也可以舍去呢?答案是可以的。

假设 $k\in[i,j]$,$sum(a,b)=\sum_{i=a}^barr[i]$,得: $sum(i,j)<0$

$\because sum(i,k)>0$ 且 $sum(i,k)+sum(k,j)=sum(i,j)$ $\therefore sum(k,j)<sum(i,j)<0$

所以如果把 $k$ 到 $j$ 的序列附加到 $j$ 之后的序列上,只会使序列越来越小。所以 $i$ 到 $j$ 的序列都可以舍去。

代码

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner sc = new Scanner(System.in);
        //sc = new Scanner(new FileInputStream("src\\oj\\hdoj\\Q1003.txt"));
        int N = sc.nextInt();

        for (int l = 0; l < N; l++) {
            int n = sc.nextInt();
            int max = -999999, sum = 0, ci = 0, i = 0, j = 0;

            for (int k = 0; k < n; k++) {
                int tmp = sc.nextInt();
                if (sum < 0){
                    sum = tmp;
                    ci = k;
                } else {
                    sum += tmp;
                }
                if (sum > max){
                    j = k;
                    i = ci;
                    max = sum;
                }
            }

            System.out.println("Case " + (l+1) + ":" );
            System.out.println(max + " " + (i + 1) + " " + (j + 1));

            if (l < N - 1) {
                System.out.println();
            }
        }
    }
}

Read more

Volcano 与 Kubernetes GPU 调度学习笔记

本笔记系统整理 Volcano 调度器、Kubernetes 调度框架、GPU Device Plugin、HAMi 等云原生 AI 调度领域的核心知识,适合用于学习、复习和工程实践参考。 目录 * 第一部分:Volcano 入门 * 1. Volcano 是什么 * 2. 安装与快速使用 * 3. 核心特性一览 * 第二部分:Volcano 整体架构 * 4. Volcano 解决的核心问题 * 5. 整体架构与数据流 * 6. 三层抽象模型 * 第三部分:Volcano 核心实现原理 * 7. Session 机制 * 8. Gang Scheduling 实现 * 9. Queue 与 DRF 公平调度

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd