P1115 最大子段和

题目传送门

一、暴力法

5 个测试点,全部TLE

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
const int INF = 0x3f3f3f3f;
int res = -INF;
int a[N];
int n;
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++)       //枚举起点
        for (int j = i; j <= n; j++) { //枚举终点
            int sum = 0;
            for (int k = i; k <= j; k++) sum += a[k]; //区间内的和
            res = max(res, sum);
        }
    cout << res << endl;
    return 0;
}

二、前缀和

5 个测试点,3TLE

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
const int INF = 0x3f3f3f3f;
int n, a[N], res = -INF;
int sum[N];
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];                //输入
        sum[i] = sum[i - 1] + a[i]; //记录前缀和
    }
    for (int i = 1; i <= n; i++)
        for (int j = i; j <= n; j++)
            res = max(res, sum[j] - sum[i - 1]);

    cout << res << endl;
    return 0;
}

三、分治法

AC掉本题

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200010;
int n;
int a[N];
//分治函数
int rec(int l, int r) {
    //递归的出口
    if (l == r) return a[l]; // l=r时,直接返回该位置的值
    int mid = (l + r) >> 1;  //中间点
    // [l..mid]区间内包含mid的最大后缀和
    int sum = 0, lmax = -INF, rmax = -INF;
    for (int i = mid; i >= l; i--) sum += a[i], lmax = max(lmax, sum);
    // [mid+1..r]区间内包含(mid+1)的最大前缀和
    sum = 0;
    for (int i = mid + 1; i <= r; i++) sum += a[i], rmax = max(rmax, sum);
    //三种可能取最大值
    return max(max(rec(l, mid), rec(mid + 1, r)), lmax + rmax);
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    printf("%d", rec(1, n)); //在范围1~n之间求最大子段和
    return 0;
}

四、动态规划法

AC

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
const int INF = 0x3f3f3f3f;
int n, a[N], f[N], res = -INF;
// 定义f[i]为从1开始,到i结束的所有数字中的最大子段和
// 从边界开始考虑,如果f[1]=a[1];
// f[2] =max(a[2],a[2]+f[1])
// 推广通用公式: f[i]=max(f[i-1]+a[i],a[i])
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];                       //输入
        f[i] = max(f[i - 1] + a[i], a[i]); // DP
        res = max(res, f[i]);              //取最大值也同时进行,节约时间
    }
    cout << res << endl;
    return 0;
}

五、线段树

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a[N];

//线段树用的结构体
struct Node {
    int l, r; // 区间的左右端点
    int lmax; // 区间内紧靠左端点的最大子段和
    int rmax; // 区间内紧靠右端点的最大子段和
    int tmax; // 区间内的最大值
    int sum;  // 区间内最大子段和
} tr[N << 2];

//求三个元素的最大值
inline int max(int a, int b, int c) {
    return max(max(a, b), c);
}

//从子孙节点汇集信息
void pushup(int u) {
    //区间和等于左儿子+右儿子的区间和
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
    // u的左侧最大子段和=max(左儿子的左侧最大子段和,左儿子的总和+右儿子的左侧最大子段和)
    tr[u].lmax = max(tr[u << 1].lmax, tr[u << 1].sum + tr[u << 1 | 1].lmax);
    // u的右侧最大子段和=max(右儿子的右侧最大子段和,右儿子的总和+左儿子的右侧最大子段和)
    tr[u].rmax = max(tr[u << 1 | 1].rmax, tr[u << 1 | 1].sum + tr[u << 1].rmax);
    //以u为根的节点,整体子段最大值 = max(左儿子子段最大值,右儿子子段最大值,左儿子右侧后缀和最大值+右儿子左侧前缀和最大值)
    tr[u].tmax = max(tr[u << 1].tmax, tr[u << 1 | 1].tmax, tr[u << 1].rmax + tr[u << 1 | 1].lmax);
}

//构建节点u,其维护的是区间[l,r]
void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r; //初始化记录左右端点
    if (l == r) {             //递归出口,只有一个元素
        //根据题意不同,写的方法不同
        // sum:区间和,就是a[l]或a[r]
        // tmax:子段和最大值,因为只有一个,也只能是 a[l]或a[r]
        // lamx:前半段后缀和最大值,只能是a[l]或a[r]
        // rmax:后半段前缀和最大值,只能是a[l]或a[r]
        tr[u].sum = tr[u].tmax = tr[u].lmax = tr[u].rmax = a[l];
        return; //递归出口
    }
    //分治思想
    int mid = (l + r) >> 1;
    //左儿子,右儿子构建
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    //儿孙构建完成后,向父亲推送更新一下信息
    pushup(u);
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    /*
    构建线段树,基于完全二叉树思想,所以根为1
    从数据结构的角度来说,线段树是用一个完全二叉树来存储对应于其每一个区间(segment)的数据。
    该二叉树的每一个结点中保存着相对应于这一个区间的信息。同时,线段树所使用的这个二叉树是用
    一个数组保存的,与堆(Heap)的实现方式相同。
    */
    build(1, 1, n);
    //输出1~n区间内的最大子段和
    cout << tr[1].tmax;
    return 0;
}
posted @   糖豆爸爸  阅读(76)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
历史上的今天:
2016-04-15 奇虎360的开源OpenResty Windows版本
2013-04-15 17、单机运行环境搭建之 --CentOS-6.4下用tcmalloc优化nginx
Live2D
点击右上角即可分享
微信分享提示