Multiplication 2
编号:0023
试题来源:AtCoder
试题描述
给定 N N N个整数 A 1 , … , A N A_1,\dotso,A_N A1,…,AN,计算 A 1 × ⋯ × A N A_1\times \dots\times A_N A1×⋯×AN。
当乘积的结果大于
1
0
18
10^{18}
1018,输出-1
而不是乘积
说明:
- 2 ≤ N ≤ 1 0 5 2\leq N\leq 10^5 2≤N≤105
- 0 ≤ A i ≤ 1 0 18 0\leq A_i \leq 10^{18} 0≤Ai≤1018
- 所有的输入都是非负整数
输入形式
N
A 1 A_1 A1 … \dots … A N A_N AN
解答算法
算法思路
首先如果输入中存在 A i = 0 A_i=0 Ai=0,那么最后乘积的结果一定是0,因此首先在读入数据的过程中判断是否存在 A i = 0 A_i=0 Ai=0。
若不存在
A
i
=
0
A_i=0
Ai=0,那么要知道该数组的乘积是个非递减数列,因为每个数
A
i
≥
1
A_i\geq 1
Ai≥1,因此如果计算到
A
j
A_j
Aj的时候,当前的result
已经大于
1
0
18
10^{18}
1018,那么继续乘下去,也是绝对会大于
1
0
18
10^{18}
1018。
关键点在于,result
不一定会在unsigned long long
的取值范围中,可能存在溢出的问题,当溢出的时候,显然一定也是大于
1
0
18
10^{18}
1018,因此注意考虑溢出的情况。
这里用到了一个很巧妙的比较
if (double(ans) - ((double)1e18 / (double) a[i]) > eps) { //避免了溢出的风险
cout << -1 << endl;
return 0;
}
只需要进行乘积的对比就可以了,eps
是一个极小数,取得值是
1
0
−
6
10^{-6}
10−6,是double
正常的误差数,这样如果超出的话,显然不满足大于eps
的条件,巧妙地避开了溢出问题
代码实现
#if 1
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
template<class T> //自定义读入函数
inline void read(T &a) {
register T x = 0, flag = 1;
register char ch;
while (!isdigit(ch = getchar())) if (ch == '-') flag = -1;
while (x = x * 10 + (ch & 15), isdigit(ch = getchar()));
a = x * flag;
}
template <class T> //自定义写入函数
inline void write(T x) {
if (x < 0)
putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
template<class T> inline bool Chkmax(T& x, const T& y) { return x < y ? x = y, true : false; } //自定义比较函数
template<class T> inline bool Chkmin(T& x, const T& y) { return x > y ? x = y, true : false; } //自定义比较函数
#define For(i, a, b) for (int i = (a); i <= (b); i++) //从小到大重复
#define Rep(i, a, b) for (int i = (a); i >= (b); i--) //从大到小重复
#define For_edge(x) for (int i = head[x]; i; i = net[i])
#define mem0(a) memset(a, 0, sizeof a) //内存复制
const int maxn = 1e5 + 20; //数组长度最大值
int n;
#define eps 1e-7
ull a[maxn]; //数组
int main() {
read(n); //读入n
For (i, 1, n) read(a[i]); //重复读入a[i]
For (i, 1, n) if (a[i] == 0) { //判断是否存在0
cout << 0; return 0;
}
ull ans = 1; //初始化结果
For (i, 1, n) {
if (double(ans) - ((double)1e18 / (double) a[i]) > eps) { //避免了溢出的风险
cout << -1 << endl;
return 0;
}
ans = ans * a[i];
}
if (ans > (long long) 1e18) cout << -1 << endl; //判断最后结果是否大于10的18次方
else cout << ans << endl;
}
#endif
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),整个过程对数组进行了两次遍历
- 空间复杂度: O ( n ) O(n) O(n)