0023Multiplication2

这篇博客介绍了如何处理给定的N个整数数组A1到AN的乘积计算问题。当乘积超过10^18时,需要输出特定提示。文章讨论了在存在0元素时的特殊情况,以及如何利用非递减数列性质和误差比较避免溢出,同时提供了算法思路、代码实现和复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 2N105
  • 0 ≤ A i ≤ 1 0 18 0\leq A_i \leq 10^{18} 0Ai1018
  • 所有的输入都是非负整数

输入形式

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 Ai1,因此如果计算到 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} 106,是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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值