1 条题解
-
0
题目理解
我们有一个整数数组 。
操作:选择任意 ,将它改为 到 之间的整数(包含端点)。
即:如果 ,可改为 中的任意整数;
如果 ,可改为 中的任意整数。目标:使数组所有元素的乘积最小,并且操作次数最少。
输出最少操作次数和一种具体操作方案。
关键观察
-
操作不改变符号方向
- 如果 是正数,只能改成 ,结果 ,不会变成负数。
- 如果 是负数,只能改成 ,结果 ,不会变成正数。
因此,数组里每个数在操作后符号不变或变成 。
-
绝对值不会增大
操作后 ,所以整个数组的乘积的绝对值不会增大。 -
符号变化规则
原来乘积为正 操作后乘积 (只能变成 或正数)。
原来乘积为负 操作后乘积 (只能变成 或负数)。
原来乘积为 始终是 。
最小乘积 的求法
-
如果数组中有 ,那么乘积已经为 ,这是可能的最小值(不能为负)。
所以 。 -
如果数组中没有 ,但乘积为正:
我们可以将一个正数改成 ,乘积变为 (比原来的正数小)。
不能得到负数(因为所有数非负或全负?等一下——正乘积来自偶数个负数,但负数可以改成 ,所以不能变负)。
因此 。 -
如果数组中没有 ,但乘积为负:
由于所有数都不能变符号(只能往 方向变,但不能变成正数),乘积的绝对值只会减小或不变,但负号保持不变。
最小的乘积就是最负的数,但因为我们不能从负数变成正数,乘积的负号永远存在。
那么最小乘积就是绝对值最大的负数乘积吗?不,因为绝对值可以缩小到 ,但一旦有一个数变成 ,乘积就是 ,这比负数大(),所以负乘积不能变小成更负的数,但可以变大成 。
因此,要使乘积最小,我们不要改成 ,因为 大于负数。
那么保持乘积为负,并且绝对值尽量大?
等等,操作只能减小绝对值,不能增大。所以初始负乘积的绝对值就是可能的最大负乘积,因此它本身已经是最小的(最负的)。
因此 就是初始乘积(负值)。
结论(分情况讨论)
设初始数组乘积为 。
情况 1:
- 操作后乘积只能 ,且不能变得更负(绝对值不增),所以 已经是最小乘积。
- 最小操作次数:(不需要操作)。
情况 2:
- 乘积已经是最小值 。
- 最小操作次数:。
情况 3:
- 可以改成 (任选一个数改成 ),这样乘积变为 ,小于正数。
- 最小操作次数:(选任意一个元素改成 )。
- 如果某个数已经是 ,那么不用操作,但这里 意味着没有 。
特殊情况检查
- 如果数组全为正数,,操作一次改成 即可。
- 如果数组全为负数,但个数为偶数,,也是操作一次改成 。
- 如果数组有正有负,但乘积为正,同样操作一次改成 。
例子验证
[155]: → 操作 1 次:改 为 。[2, 8, -1, 3]: → 操作 0 次。[-1, 0, -2, -5]:乘积有 → → 操作 0 次。[-15, -75, -25, -30]:全负,4 个,偶数个 → → 操作 1 次:改任意一个为 (比如 为 )。
输出与题目示例一致。
算法步骤
- 读入 和数组。
- 计算初始乘积 (注意可能溢出,但 ,,乘积可能极大,所以用符号判断即可,不必算精确值):
- 记录负数的个数 ,是否有 。
- 乘积为正: 且没有 。
- 乘积为负: 且没有 。
- 乘积为 :有 。
- 根据情况输出:
- 若 :输出
0。 - 若 :输出
0。 - 若 :输出
1,然后输出任意一个下标 和 。
- 若 :输出
复杂度
每个测试用例。
完整题解代码框架(C++ 风格)
#include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while (t--) { int n; cin >> n; vector<int> a(n); bool hasZero = false; int negCount = 0; for (int i = 0; i < n; i++) { cin >> a[i]; if (a[i] == 0) hasZero = true; else if (a[i] < 0) negCount++; } // 乘积为负:奇数个负数且无0 if (!hasZero && negCount % 2 == 1) { cout << "0\n"; } // 乘积为0 else if (hasZero) { cout << "0\n"; } // 乘积为正 else { cout << "1\n"; // 输出任意一个元素改成0 cout << "1 0\n"; } } return 0; } -
- 1
信息
- ID
- 6249
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者