题解:学而思编程 元素分类
【题目来源】
学而思编程:元素分类
【题目描述】
给定 \(n\) 个整数元素 \(x_i\)。现在,需要你对这 \(n\) 个整数元素进行分类。
每个元素要么划分至 \(b\) 类,要么划分至 \(c\) 类。
我们设所有 \(b\) 类元素的相加之和为 \(sum_b\),所有 \(c\) 类元素的相加之和为 \(sum_c\)。
请你计算 \(sum_b−sum_c\) 的最大可能值。
如果某类元素的个数为 \(0\),则该类元素的相加之和视为 \(0\)。
【输入】
第一行包含整数 \(n\)。
第二行包含 \(n\) 个整数 \(x_i\)。
【输出】
输出一个整数,表示 \(sum_b−sum_c\) 的最大可能值。
【输入样例】
3
1 -2 0
【输出样例】
3
【核心思想】
-
问题分析:给定 \(n\) 个整数 \(x_i\),每个元素必须分配到 \(b\) 类或 \(c\) 类。设 \(sum_b\) 为 \(b\) 类元素之和,\(sum_c\) 为 \(c\) 类元素之和,求 \(sum_b - sum_c\) 的最大值。这是一个贪心分类问题,关键在于分析每个元素对目标函数的贡献,确定最优分配策略。
-
算法选择:
- 贪心策略(Greedy Choice):对每个元素独立决策,使其对 \(sum_b - sum_c\) 的贡献最大化
- 正负分离:根据元素的符号特征,分别归入不同类别以最大化差值
-
关键步骤:
- 初始化:\(sum_1 = 0\)(用于累加非负数),\(sum_2 = 0\)(用于累加负数)
- 遍历元素:对于每个 \(x_i\):
- 若 \(x_i \ge 0\):归入 \(b\) 类,\(sum_1 \leftarrow sum_1 + x_i\)
- 选择理由:非负数放入 \(sum_b\) 直接增加差值,放入 \(sum_c\) 会减小差值(或不变)
- 若 \(x_i < 0\):归入 \(c\) 类,\(sum_2 \leftarrow sum_2 + x_i\)
- 选择理由:负数放入 \(sum_c\) 时,\(sum_b - sum_c = sum_b - (\text{负数}) = sum_b + |x_i|\),相当于增加差值;若放入 \(sum_b\) 则会减小差值
- 若 \(x_i \ge 0\):归入 \(b\) 类,\(sum_1 \leftarrow sum_1 + x_i\)
- 计算答案:\(ans = sum_1 - sum_2\)
- 由于 \(sum_2\) 为负数,\(sum_1 - sum_2 = sum_1 + |sum_2|\),即所有非负数绝对值之和加上所有负数绝对值之和
- 输出结果:\(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),只需一次遍历所有元素
- 空间复杂度:\(O(1)\),仅使用 \(sum_1\)、\(sum_2\) 两个累加器
-
贪心决策的核心思想:
- 独立最优性:每个元素对 \(sum_b - sum_c\) 的贡献只取决于自身分配,与其他元素无关。因此可以逐个独立做最优决策
- 贡献分析:将 \(x_i\) 放入 \(b\) 类贡献为 \(+x_i\),放入 \(c\) 类贡献为 \(-x_i\)。为使 \(sum_b - sum_c\) 最大,每个元素应选择贡献更大的方向:
- 若 \(x_i \ge 0\):\(+x_i \ge -x_i\),选 \(b\) 类
- 若 \(x_i < 0\):\(-x_i > +x_i\)(负数的相反数为正),选 \(c\) 类
- 全局最优性:局部最优决策的累加即为全局最优,无后效性和冲突性
- 零的特殊处理:\(x_i = 0\) 时放入任一类贡献均为 \(0\),归入 \(sum_1\) 不影响结果
- 适用于每个元素独立贡献、无交叉约束的最大化/最小化分配问题
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, sum1, sum2; // sum1: 正数和, sum2: 负数和
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int num;
cin >> num;
// 根据正负分别累加
if (num >= 0)
{
sum1 += num; // 累加正数
}
else
{
sum2 += num; // 累加负数
}
}
// 输出正数和减去负数和
cout << sum1 - sum2 << endl;
return 0;
}
【运行结果】
3
1 -2 0
3
浙公网安备 33010602011771号