⌛️
Math ☁️
上一题链接: C/C++百题打卡[3/100]——约瑟夫问题⭐️⭐️⭐️.
下一题链接: C/C++百题打卡[5/100]——合唱队形 [考查 数组 ]⭐️⭐️⭐️⭐️.
百题打卡总目录: 🚧 🚧 …
一、题目总述
● 有一个初始长度为 n n n 的序列 a a a。你需要进行 n − 1 n-1 n−1 次操作。每一次操作前先在当前序列中选出两个相邻的数 x , y x,y x,y 并删除(原序列中 x x x 在 y y y 左边),再往原位置插入一个数值为 x + y x+y x+y 或一个 x − y x−y x−y 的数。 n − 1 n−1 n−1 次操作之后最终只会剩下恰好一个数,求这个剩下的数的最大值。
● 输入描述:
第一行,一个整数
n
n
n。
第二行,共
n
n
n 个整数
a
i
a_i
ai,(
i
=
1
,
2
,
.
.
.
,
n
i=1,2,...,n
i=1,2,...,n)
● 输出描述:
共一行,一个整数,表示答案。
注意:对于 100 100% 100 的数据, 1 ≤ n ≤ 1 0 5 , ∣ a i ∣ ≤ 1 0 9 1 ≤ n ≤ 10 1\le n\le 10^5 ,|a_i|\le 10^91≤n≤10 1≤n≤105,∣ai∣≤1091≤n≤10 ,运行限制——>最大运行时间: 1 s 1s 1s,最大运行内存: 128 M 128M 128M
● 输入样例:
5
-1 1 1 -1 1
● 输出样例:
3
● 样例解释【一种操作过程如下:】
-1 1 1 -1 1
-1 1 1 -2
-1 1 3
-1 4
3
可以证明没有更优的方案。
二、思考空白区
● 题目难度:⭐️⭐️
● 建议思考时间:⌛️ ⌛️
三、题目解析
● 这是一道考查数学推导
的题。可以益智大脑。
● 主要就是要做到 “认真分析题目
+ 数学的证明
”。
- 这里,我采用
数学归纳法
来解析这道题。(好像是叫这种方法吧,叫数学推导法
也行,也就是一步步推导过来的)
① 首先,如果n=1
,只有一个数字,那么直接输出就行了。
② 如果n=2
,且不管 a 1 a_1 a1 的正负,若 a 2 ≥ 0 a_2≥0 a2≥0,那么 a 1 + a 2 a_1+a_2 a1+a2 就是最大数。
③ 如果n=2
,且不管 a 1 a_1 a1 的正负,若 a 2 < 0 a_2<0 a2<0,那么 a 1 − a 2 a_1- a_2 a1−a2 就是最大数。
● 以上就是基础推导部分
,也容易理解。
● 但当 n ≥ 3 n≥3 n≥3 时,比如对于序列 “ a 1 、 a 2 、 a 3 a_1、a_2、a_3 a1、a2、a3 ” 来说,是先执行 “ a 1 与 a 2 a_1与a_2 a1与a2 的加法/减法 ”,还是先执行 “ a 2 与 a 3 a_2与a_3 a2与a3 的加法/减法 ” 呢?
● 如果你这么想,那么问题就复杂了,你想想,要是n>10000
的话,那不得考虑得好复杂。
- 所以,我们需要转换一下思维。只需要想通下面这一点,那么这道题就迎刃而解了。
[1] 如果我们有一个长度n>3
的序列 a 1 、 a 2 、 a 3 、 . . . 、 a n a_1、a_2、a_3、...、a_n a1、a2、a3、...、an,我们一定可以人为地把它们划分成两堆。
[2] 一堆是 { a 1 、 a 2 、 a 3 、 . . . 、 a n − 1 } \{a_1、a_2、a_3、...、a_{n-1} \} {a1、a2、a3、...、an−1},另一堆是 { a n } \{a_n \} {an}。
[3] 很显然,前面一堆通过计算最终会得到一个数,它和后面一堆里面的 a n a_n an 刚好就形成了前面基础推导部分
的 ②③ 中的一个。那么就确定了 a n a_n an 前面的符号(+或者-)
[4] 同理,我们也可以继续对 { a 1 、 a 2 、 a 3 、 . . . 、 a n − 1 } \{a_1、a_2、a_3、...、a_{n-1} \} {a1、a2、a3、...、an−1} 进行拆分,拆分成 { a 1 、 a 2 、 . . . 、 a n − 2 } \{a_1、a_2、...、a_{n-2} \} {a1、a2、...、an−2} 和 { a n − 1 } \{a_{n-1} \} {an−1} 然后操作步骤就同理 [3] 了。也就确定了 { a n − 1 } \{a_{n-1} \} {an−1} 前面的符号(+或者-)
[5] 最后依次类推即可。
● 此时,问题就简化为:只要是负数,对它进行 “减操作”;只要是非负数,对它进行 “加操作” 即可得到最后的最大数。
四、完整代码(C和C++版)
● C 语言版本:【边输入,就边在计算最终结果】
#include<stdio.h>
int main()
{
int n, x;
long long int ans = 0; // 一定要开得足够大
scanf("%d", &n);
scanf("%d", &x); // 读取第一个数
ans = x; // 答案赋初始值
for (int i = 1; i < n; i++)
{
scanf("%d", &x); // 读取后续的数字,并做后续从处理, 实时更新答案
if (x < 0) // 是负数就减
{
ans -= x;
}
else // 是正数就加
{
ans += x;
}
}
cout << ans;
return 0;
}
● 补充说明:int
默认是占4
个字节,数值表示范围是:-2147483648 ~ 2147483647
,大约21
亿。在大多情况下int
类型都是可以满足需要的,但不乏有特殊情况【比如这道题】,这时就要用到long long int
类型了,其取值范围是:-9223372036854775808 ~ 9223372036854775807
。这个数值范围还是很大,它在内存中占用8
个字节。
● 运行结果:
● C++ 版本:
#include<iostream>
using namespace std;
int main()
{
int n, x;
long long int ans = 0;
cin >> n;
cin >> x; // 读取第一个数
ans = x; // 答案赋初始值
for (int i = 1; i < n; i++)
{
cin >> x; // 读取第二个数,并做后续从处理, 实时更新答案
if (x < 0) // 是负数就减
{
ans -= x;
}
else // 是正数就加
{
ans += x;
}
}
cout << ans;
return 0;
}
五、做题小结与反思
● 一般很复杂的数学问题,只要从最开始,最简单的情况开始分析,然后把后面复杂的情况尽可能地归化到最简单的情况即可。
六、参考附录
[1] 原题地址:https://www.luogu.com.cn/problem/P7917.
上一题链接: C/C++百题打卡[3/100]——约瑟夫问题⭐️⭐️⭐️.
下一题链接: C/C++百题打卡[5/100]——合唱队形 [考查 数组 ]⭐️⭐️⭐️⭐️.
百题打卡总目录: 🚧 🚧 …
C/C++百题打卡[4/100]——融合最大数 [题目源自 洛谷 ] ⭐️⭐️
标签:数学
因为最近有重要考试所以缓更一周
2021/12/12