题目
代码1(队列)
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
const int N = 1e5+10;
int n;
long long a[N];
queue<PII> q;
long long sum[25];
int main()
{
memset(sum, -1, sizeof(long long) * 25);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
q.push({1, 1});
while(!q.empty())
{
int fr = q.front().x;
int deep = q.front().y;
if(sum[deep] == -1) sum[deep] = 0;
sum[deep] += a[fr];
int left = 2 * fr;
int right = 2 * fr + 1;
if(left <= n)
{
q.push({left, deep+1});
}
if(right <= n)
{
q.push({right, deep+1});
}
q.pop();
}
long long MAX = LLONG_MIN, mdeep;
for(int i = 1; i <= 20; i++)
{
if(sum[i] != -1 && sum[i] > MAX) //sum[i]有负数,而且不知道i的具体范围,初始化又是0,所以需要用-1初始化
{
MAX = sum[i];
mdeep = i;
}
}
cout << mdeep;
return 0;
}
代码2(前缀和)
#include <iostream>
// 使用命名空间 std,这样就可以不用每次调用 std:: 来访问标准库函数
using namespace std;
// 定义一个别名 LL,它代表 long long 类型,通常用于表示大整数
typedef long long LL;
// 定义一个常量 N,其值为 100010,作为数组的最大长度
const int N = 100010;
// 定义全局变量
// a[] 数组存储输入的整数序列
// id 用来记录答案中子数组的深度
// n 为整数序列的长度
// ans 用来记录当前找到的最大和,初始化为 INT_MIN
// s[] 数组用于存储前缀和
int a[N], id, n;
LL ans = INT_MIN, s[N];
// 主函数
int main()
{
// 读入整数序列的长度 n
cin >> n;
// 读入整数序列并存入 a[] 数组
for (int i = 1; i <= n; i++)
cin >> a[i];
// 计算前缀和 s[]
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + a[i];
// 遍历每个可能的子数组长度,从 2 开始,每次翻倍直到超过 n
// j 为当前子数组的起始位置
// depth 表示子数组的 "深度",即进行了几次翻倍操作
for (int i = 2, j = 1, depth = 1; j <= n; j = i, i *= 2, depth++)
{
// 确保 i 不会越界
i = min(i, n + 1);
// 如果当前子数组的和大于之前找到的最大和,则更新 ans 和 id
if (ans < s[i - 1] - s[j - 1])
{
ans = s[i - 1] - s[j - 1];
id = depth;
}
}
// 输出答案中子数组的深度
cout << id << endl;
return 0;
}
上面这个抓住前缀和的r用下一层深度的第一个节点来算r = i-1,l就是当前深度的第一个节点。
因此i的迭代用i*=2,j滞后于i,j=i
对于根节点,s[1] - s[0],r = 1, l = 0,令i = 2, j = 1。
因为这个是完全二叉树,最后一层不一定满,因此最后一个i应该要强制规定到n+1。而j一定是某层第一个节点。最后一次j必定为n+1,所以只要控制 j <= n,以此作循环持续标志就行。
代码3(双指针,其实就是没有前缀和而已)
#include <bits/stdc++.h>
// 使用命名空间 std,这样就可以不用每次调用 std:: 来访问标准库函数
using namespace std;
// 定义一个别名 LL,它代表 long long 类型,通常用于表示大整数
typedef long long LL;
// 定义一个常量 N,其值为 100010,作为数组的最大长度
const int N = 100010;
// 定义全局变量
// a[] 数组存储输入的整数序列
// id 用来记录答案中子数组的深度
// n 为整数序列的长度
// ans 用来记录当前找到的最大和,初始化为 INT_MIN
// s[] 数组用于存储前缀和
int a[N], id, n;
LL ans = INT_MIN;
// 主函数
int main()
{
// 读入整数序列的长度 n
cin >> n;
// 读入整数序列并存入 a[] 数组
for (int i = 1; i <= n; i++)
cin >> a[i];
// 遍历每个可能的子数组长度,从 2 开始,每次翻倍直到超过 n
// j 为当前子数组的起始位置
// depth 表示子数组的 "深度",即进行了几次翻倍操作
for (int i = 2, j = 1, depth = 1; j <= n; j = i, i *= 2, depth++)
{
// 确保 i 不会越界
i = min(i, n + 1);
LL s = 0;
// 计算从 j 到 i - 1 的所有元素之和
for(int m = j; m <= i-1; m++) s += a[m];
// 如果当前子数组的和大于已有的最大值,则更新最大值和深度
if(ans < s)
{
ans = s;
id = depth;
}
}
// 输出答案中子数组的深度
cout << id << endl;
return 0;
}