洛谷 P1115 最大子段和

【题目链接】

洛谷 P1115 最大子段和

【题目考点】

1. 动态规划:线性动规
  • 最大子段和

【解题思路】

解法1:线性动规

子段或子串指的是序列中连续的多个元素,子序列是指序列中可以不连续的多个元素。

1. 状态定义

集合:所有子段
限制:子段所在区间范围
属性:加和
条件:最大
统计量:加和
状态定义dp[i]为以i为结尾的加和最大的子段的加和。

2. 状态转移方程

a[i]为第i个元素的值
分割集合:考虑以第i元素为结尾的子段

  • 子集1:第i元素自己作为一个子段,dp[i] = a[i]
  • 子集2:如果第i元素和其左边一些元素构成子段,那么第i元素左边的子段为以第i-1元素为结尾的子段,以第i-1元素为结尾的子段中加和最大的子段的加和为dp[i-1],在这个子段后面添加第i元素,这个以i为结尾的子段加和为dp[i]=dp[i-1]+a[i]

以上二者取最大值。

3. 结果输出

题目要求的是最大子段和,就是以每个位置为结尾的最大子段和中的最大值,即为求dp数组的最大值。

解法2:前缀和

求出原序列的前缀和,保存在s数组中。s[i]表示前i个数的和。
最大子段和即为:满足j>is[j]-s[i]的最大值。
mi为数组s在下标1~i中的最小值。那么s[i]-mi即为以i为结尾的子段的最大子段和。
最大子段和为以每个位置为结尾的最大子段和中的最大值。
i从1循环到n,不断更新mi,保持mi为s[1]~s[i]中的最小值,求s[i]-mi的最大值。

解法3:双指针

顺序遍历数组,当前已经选择了以第i-1元素为结尾的子段,该子段和为s,看是否把第i个元素a[i]加入到子段中。

  • 如果已有子段的和为负数,即s<0,那么s+a[i] < a[i],也就是说第i元素自己作为一个子段的加和,要比将其接到前一个子段的末尾构成的子段的加和要大。所以此时子段和应该从s变为a[i]
  • 如果已有的子段和大于等于0, 即s>=0,那么s+a[i] >= a[i],应该将第i元素接到前一个子段的末尾,构成以第i元素为结尾的子段,子段和从s变为s+a[i]

在遍历过程中,求所有出现的子段和的最大值。

该算法实际是一种双指针(尺取法)算法,子段的第一个元素的下标为l,最后一个元素的下标为r,当前子段和为负数,则l改变,否则r改变。这里思考时借助了双指针的思想,但代码中并不需要写出双指针。

【题解代码】

解法1:线性动规
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define INF 0x3f3f3f3f 
int a[N], dp[N];//dp[i]:以i为结尾的加和最大的子段的加和
int main()
{
    int n, mx = -INF;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    for(int i = 1; i <= n; ++i)
        dp[i] = max(a[i], a[i] + dp[i-1]);
    for(int i = 1; i <= n; ++i)
        mx = max(mx, dp[i]);
    cout << mx;
    return 0;
}

解法2:前缀和
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define INF 0x3f3f3f3f
int a[N], s[N];
int main()
{
    int n, mi = 0, mxSum = -INF;
    cin >> n;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        s[i] = s[i-1] + a[i];
    }
    for(int i = 1; i <= n; ++i)
    {
        mxSum = max(mxSum, s[i] - mi);
        mi = min(mi, s[i]);
    }
    cout << mxSum;
    return 0;
}
解法3:双指针
#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define INF 0x3f3f3f3f
int a[N], s;
int main()
{
    int n, mi = 0, mxSum = -INF;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    for(int i = 1; i <= n; ++i)
    {
        if(s < 0)
            s = a[i];
        else
            s += a[i];
        mxSum = max(mxSum, s);
    }
    cout << mxSum;
    return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷 P1681 最大正方形II 是一个动态规划问题,要给定一个由 '0' 和 '1' 组成的矩阵,找出其中最大的正方形,并输出其边长。 以下是一个 C++ 编写的解答示例: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int maximalSquare(vector<vector<char>>& matrix) { int rows = matrix.size(); if (rows == 0) return 0; int cols = matrix[0].size(); vector<vector<int>> dp(rows + 1, vector<int>(cols + 1, 0)); int maxSide = 0; for (int i = 1; i <= rows; i++) { for (int j = 1; j <= cols; j++) { if (matrix[i-1][j-1] == '1') { dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1; maxSide = max(maxSide, dp[i][j]); } } } return maxSide * maxSide; } int main() { int n, m; cin >> n >> m; vector<vector<char>> matrix(n, vector<char>(m)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> matrix[i][j]; } } cout << maximalSquare(matrix) << endl; return 0; } ``` 在上述代码中,我们首先定义了一个名为 `maximalSquare` 的函数,该函数接受一个二维字符矩阵 `matrix` 作为参数,返回最大正方形的边长。 在 `main` 函数中,我们首先从标准输入读取矩阵的行数和列数,并创建一个大小为 `n x m` 的二维字符矩阵。然后,我们按行读取矩阵的数据,并调用 `maximalSquare` 函数进行解。最后,输出最大正方形的边长。 在动态规划的解法中,我们使用一个二维数组 `dp` 来记录以当前位置为右下角的最大正方形的边长。遍历矩阵中的每个元素,如果当前元素为 '1',则根据其左方、上方和左上方的最大正方形边长计算出当前位置的最大正方形边长,并更新 `dp` 数组和最大边长变量。 请注意,以上代码仅为示例,可能需要根据具体题目要进行适当修改。同时,为了简化示例,未进行输入验证,请确保输入的矩阵符合题目要

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值