P1115 最大子段和
一、暴力法
个测试点,全部
#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;
}
二、前缀和
个测试点,个
#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;
}
三、分治法
掉本题
#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;
}
四、动态规划法
#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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2016-04-15 奇虎360的开源OpenResty Windows版本
2013-04-15 17、单机运行环境搭建之 --CentOS-6.4下用tcmalloc优化nginx