蓝桥杯2024年第十五届省赛真题-R 格式(高精度乘法 + 加法)

本文介绍了一种C语言实现的高精度乘法和加法模板,用于处理double类型与2的幂相乘并进行四舍五入的问题,包括高精度乘法函数`mul`和高精度加法函数`ADD`的详细实现过程。

本题链接:蓝桥杯2024年第十五届省赛真题-R 格式 - C语言网

题目:

样例:

输入
2 3.14
输出
13

思路:

        根据题意,结合数据范围,这是一道模板的高精度乘以低精度问题。

        题意是double 类型 d 与 2 的n 次幂相乘,最后四舍五入。

        我们换一下视角,看作 对于double 类型 d ,与 n 个 2 相乘,最后四舍五入。

        这样就很快解出答案了,就是对于double小数部分需要微调一下即可。

高精度乘法模板函数如下:

        

// 高精度乘以低精度 模板
inline string mul(string A,int x)
{
	vector<int>a;	// 取出每一位
	int len = A.size();	// 倒着取每一位,方便低位到高位的计算
	
	for(int i = len - 1;~i;--i) a.emplace_back(A[i] - '0');
	
	vector<int>tem;	// 存储计算结果
	int t = 0;	// 存储临时进位数
	for(int i = 0;i < len;++i)
	{
		int num = t + a[i] * x;	// 开始每一位相乘
		tem.emplace_back(num % 10);	// 取计算结果个位
		t = num / 10;	// 计算进位
	}
	
	while(t > 0)
	{
		// 如果最后有高位的进位,我们进位
		tem.emplace_back(t % 10);
		t /= 10;
	}	
	
	string ans = "";	// 存储最后计算结果
	// 将结果转回为 string ,并返回结果
	int temlen = tem.size();
	for(int i = temlen - 1;~i;--i)	ans += char(tem[i] + '0');
	return ans;
}

  高精度加法模板函数如下:

// 高精度加法模板
inline string ADD(string A,string B)
{
	vector<int>a,b;
	int alen = A.size();
	int blen = B.size();// 倒着取每一位,方便低位到高位的计算
	for(int i = alen - 1;~i;--i) a.emplace_back(A[i] - '0');
	for(int i = blen - 1;~i;--i) b.emplace_back(B[i] - '0');
	
	vector<int>tem;	// 存储计算结果
	int t = 0;	// 存储临时进位数
	for(int i = 0;i < alen or i < blen;++i)
	{
		int num = t;	// 开始每一位相加
		if(i < alen) num += a[i];
		if(i < blen) num += b[i];
		tem.emplace_back(num % 10);// 取计算结果个位
		t = num / 10;// 计算进位
	}
	
	while(t > 0)
	{
		// 如果最后有高位的进位,我们进位
		tem.emplace_back(t % 10);
		t /= 10;
	}	
	
	string ans = "";	// 存储最后计算结果
	
	// 将结果转回为 string ,并返回结果
	int temlen = tem.size();
	for(int i = temlen - 1;~i;--i)	ans += char(tem[i] + '0');
	return ans;
}

代码详解如下:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#define endl '\n'
#define int long long
#define YES puts("YES")
#define NO puts("NO")
#define umap unordered_map
#define All(x) x.begin(),x.end()
#pragma GCC optimize(3,"Ofast","inline")
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
using namespace std;
const int N = 2e6 + 10;
inline void solve();

signed main()
{
//	freopen("a.txt", "r", stdin);
//	IOS;
	int _t = 1;
//	cin >> _t;
	while (_t--)
	{
		solve();
	}
	return 0;
}

// 高精度乘以低精度 模板
inline string mul(string A,int x)
{
	vector<int>a;	// 取出每一位
	int len = A.size();	// 倒着取每一位,方便低位到高位的计算
	
	for(int i = len - 1;~i;--i) a.emplace_back(A[i] - '0');
	
	vector<int>tem;	// 存储计算结果
	int t = 0;	// 存储临时进位数
	for(int i = 0;i < len;++i)
	{
		int num = t + a[i] * x;	// 开始每一位相乘
		tem.emplace_back(num % 10);	// 取计算结果个位
		t = num / 10;	// 计算进位
	}
	
	while(t > 0)
	{
		// 如果最后有高位的进位,我们进位
		tem.emplace_back(t % 10);
		t /= 10;
	}	
	
	string ans = "";	// 存储最后计算结果
	// 将结果转回为 string ,并返回结果
	int temlen = tem.size();
	for(int i = temlen - 1;~i;--i)	ans += char(tem[i] + '0');
	return ans;
}

// 高精度加法模板
inline string ADD(string A,string B)
{
	vector<int>a,b;
	int alen = A.size();
	int blen = B.size();// 倒着取每一位,方便低位到高位的计算
	for(int i = alen - 1;~i;--i) a.emplace_back(A[i] - '0');
	for(int i = blen - 1;~i;--i) b.emplace_back(B[i] - '0');
	
	vector<int>tem;	// 存储计算结果
	int t = 0;	// 存储临时进位数
	for(int i = 0;i < alen or i < blen;++i)
	{
		int num = t;	// 开始每一位相加
		if(i < alen) num += a[i];
		if(i < blen) num += b[i];
		tem.emplace_back(num % 10);// 取计算结果个位
		t = num / 10;// 计算进位
	}
	
	while(t > 0)
	{
		// 如果最后有高位的进位,我们进位
		tem.emplace_back(t % 10);
		t /= 10;
	}	
	
	string ans = "";	// 存储最后计算结果
	
	// 将结果转回为 string ,并返回结果
	int temlen = tem.size();
	for(int i = temlen - 1;~i;--i)	ans += char(tem[i] + '0');
	return ans;
}

inline void solve()
{
	
	int n;
	string d;
	bool vis = false;	// 检查是否符合四舍五入
	cin >> n >> d;
	
	// 如果 d 是整数的情况
	if((int)d.find(".") == -1)
	{
		while(n--)
		{
			d = mul(d,2);
		}
	}else
	{
		int pos = d.find(".");	// 找到 小数点 . 的位置下标
		
		int len = d.size() - pos - 1;	// 计算小数部分长度
		d.erase(d.begin() + pos);	// 删掉 小数点 .

		// 将 d 所有的数字当作整数相乘计算
		while(n--)
		{
			d = mul(d,2);
		}
		
		pos = d.size() - len;	// 更新小数点位置
		
		// 查看小数点后一位是否符合四舍五入
		if((d[pos] - '0') >= 5) vis = true;
		
		// 删掉小数部分,保留整数
		while(len--) d.erase(d.begin() + d.size() - 1);
		
		if(vis) d = ADD(d,"1");	// 如果符合四舍五入,那么进一
	}
	cout << d << endl;	
}

最后提交:

<think>首先,用户的问题是关于“最长连续子数列”的解法或相关算法,具体是蓝桥杯题目。用户提到“最长连续子数列”,但根据上下文,这可能指的是“最长连续子序列”或类似问题。在引用中,引用[3]提到了一个区间问题:满足乘积等于和的区间数量。用户的问题可能与此相关,但用户明确说“最长连续子数列”,所以我需要澄清。 用户说:“我想寻找最长连续子数列的解法或相关算法 请问蓝桥杯 最长连续子数列 算法 解题思路”。在引用中,引用[3]是关于“问有多少个区间 [L,R] 满足区间内元素的乘积等于他们的和”,这类似于“最大子数组和”或“最大子数组积”的变体,但用户说“最长连续子数列”,可能指的是最长连续子序列,如最长递增子序列(LIS)或最长公共子序列(LCS),但“连续”是关键。 “连续子数列”意味着子序列必须是连续的,即子数组。所以,用户可能在问关于最长连续子数组的问题,比如最大子数组和(Kadane's algorithm),但用户没有指定条件。用户提到了“蓝桥杯”,所以从引用中,引用[3]似乎是最相关的,因为它涉及子数组的乘积和和和。 在引用[3]中:"问有多少个区间 [L,R] 满足区间内元素的乘积等于他们的和",这是一个计数问题,而不是“最长”的问题。用户的问题是“最长连续子数列”,所以可能用户误解了问题,或者我需要基于上下文推断。 用户说:“我想寻找最长连续子数列的解法或相关算法”,并提到“蓝桥杯”。在引用[1]中,有“#D 最小权值记忆化搜索动态规划”,但没具体说明。引用[2]是DFS用于飞机降落问题。引用[3]是区间乘积和和和相等的问题。引用[4]是括号序列问题。 或许用户指的是引用[3]中的问题,但用户说“最长”,而引用[3]是“有多少个”,不是“最长”。用户可能记错了,或者“最长连续子数列”是另一个问题。 在蓝桥杯中,可能有多个问题。用户说“最长连续子数列”,所以我应该提供关于最长连续子数组的通用算法,如最大子数组和。 但用户引用了引用[3],所以我应该优先考虑那个。 用户消息:"我想寻找最长连续子数列的解法或相关算法 请问蓝桥杯 最长连续子数列 算法 解题思路",并且引用包括[3]。 所以,我假设用户指的是类似引用[3]的问题,但“最长”可能是个错误,或者我需要解释。 为了安全起见,我可以先解释最长连续子数组的通用算法,然后连接到引用[3]的问题。 系统指令:回答中引用的段落末尾自然地添加引用标识,例如:[^1] 所以,当我引用时,需要添加引用标识。 用户要求生成相关问题。 系统指令:生成相关问题。 在回答结束时,添加相关问题。 回答结构:清晰,逐步解决问题。 首先,澄清问题:用户可能指的是“最长连续子序列”或“子数组”,但“连续”意味着子数组。 在算法中,常见问题有: - 最大子数组和:使用Kadane算法- 最长连续递增子序列:简单遍历。 但用户提到“蓝桥杯”,引用[3]是具体问题:乘积等于和。 所以,我应针对引用[3]的问题提供解题思路,即计数满足乘积等于和的区间数量。 但用户说“最长”,不是“计数”。用户说“最长连续子数列”,所以可能用户想要最长的这样的区间。 在引用[3]中,是“有多少个”,但我们可以扩展为找到最长的区间满足乘积等于和。 是的,那样更合理。 所以,定义问题:给定数组,找到最长的连续子数组(子数列),使得元素的乘积等于元素的和。 即,对于子数组 a[L..R],有 prod = sum。 然后,提供解题思路。 首先,数学上,当所有元素为1时,乘积和和相等,因为1*1*...*1 = 1+1+...+1。 或者当有0时,但如果有0,乘积为0,但和可能不为0,除非所有为0,但0+0=0,0*0=0。 但更常见的是,当元素为1或2时,例如[1,2]:1*2=2,1+2=3,不相等;[1,1]:1*1=1,1+1=2,不相等?等等。 对于[1,1]:prod=1*1=1,sum=1+1=2,1≠2。 对于单个元素,比如x,prod=x,sum=x,所以相等。 对于两个元素,a和b,prod=a*b,sum=a+b,设a*b=a+b,则a*b - a - b =0,所以(a-1)(b-1)=1。 因此,只有当a和b都大于1且(a-1)(b-1)=1时,或者a=1,b=任意,但a=1,b任意:prod=b, sum=1+b, 所以b=1+b 仅当b=0,但0可能无效。 设 a*b = a+b。 然后 a*b - a - b =0 添加1: a*b - a - b +1 =1 所以 (a-1)(b-1)=1。 因此,对于两个元素,当 (a-1)(b-1)=1 时,乘积等于和。 如果 a 和 b 是整数,那么 (a-1)(b-1) 必须是同时为 1 或同时为 -1,但负数可能不被考虑,所以 a-1=1, b-1=1,所以 a=2, b=2。 [2,2]:prod=4,sum=4,相等。 或者 a-1=-1, b-1=-1,所以 a=0, b=0,但 [0,0]:prod=0,sum=0。 但通常,数组元素是正整数,所以 [2,2] 是可行的。 对于三个元素,情况更复杂。 但一般来说,对于子数组,只有当所有元素为 1,或者一个元素,或者两个 2 等等时,乘积才可能等于和。 但更常见的是,对于大的子数组,如果所有元素都是 1,那么 prod=1,sum=长度,所以 1=长度 仅当长度=1。 所以对于长度大于 1 的数组,只有当所有元素为 1 时,prod=1 但 sum=长度,所以 1=长度 仅当长度=1,矛盾。因此对于长度>1,如果所有元素都是 1,prod 和 sum 不相等,除非长度=1。 对于全是 1 的长度为 k 的数组:prod=1,sum=k,所以当 k=1 时,1=1,成立;当 k>1 时,1 ≠ k。 所以对于长度大于 1 的情况,只有当有 2 或其他数字时,乘积和和才可能相等。 例如 [2,2]:prod=4,sum=4。 [1,2,3]:prod=6,sum=6,成立。 1*2*3=6,1+2+3=6。 [1,1,2,4]:prod=1*1*2*4=8,sum=1+1+2+4=8。 等等。 但数组中的数字可能很大,所以我们需要一个算法来找到所有元素乘积等于所有元素和的子数组。 但用户询问的是“最长”的这类子数组,所以是最大长度。 在引用[3]中,是计数,但类似。 现在,对于解题思路。 直接的方法是检查所有可能的子数组,对于每个子数组,计算乘积和和,并检查是否相等。但这是 O(n^2) 个子数组,每个子数组的乘积和和计算是 O(n),所以总时间是 O(n^3),对于大的 n 来说效率不高。 我们需要优化。 一种方法是注意到,对于正整数,当子数组包含小数字(如1、2、3)时,乘积和和才可能相等,但如果有大数字,乘积会增长得非常快,因此和可能无法跟上,除非数字很小。 具体地,对于正整数,如果子数组中的最大值是 M,那么对于长度 k,有 sum <= k*M,而 prod >= M(如果所有元素都 >=1),但更相关的是,当元素都 >=2 时,prod 至少是 2^k,而 sum <= k*M,所以当 k 很大时,除非 M 很小,否则 2^k > k*M。 但元素可以是1,这有助于求和而不增加乘积。 实际上,满足乘积等于和的子数组可能很少,并且长度有限。 事实上,对于固定范围的值,当所有元素为1时,最大长度是有限的,但如上所述,对于所有元素为1的情况,仅当 k=1 时成立。 当元素为1或2时,等等。 但为了高效地找到所有这样的子数组,或者最长的子数组,我们可以利用以下事实:对于大的 k,乘积增长非常快,因此只有小 k 才可能,或者子数组必须包含小数字。 在算法上,一种更好的方法是遍历起点,然后扩展终点,同时维护乘积和和。 但乘积可能变得非常大,因此可能溢出,但我们可以使用对数,但由于是相等性检查,使用对数可能会有精度问题。 由于我们需要 prod == sum,并且对于正整数,当子数组包含1时,或者当数字很小时,这种情况很少见。 实际上,对于长度大于1的子数组,只有当子数组包含1时,prod 和 sum 才可能相等,因为如果所有元素都 >=2,那么对于长度 k,有 prod >= 2^k,sum <= k*M,所以当 k 很大时,2^k > k*M,因此不相等。 但如果有1,例如,如果有一个1,那么 prod 可能更小。 但无论如何,关键点是,对于给定的起点,随着我们扩展终点,sum 和 prod 都会变化,但 prod 增长更快,因此一旦 prod 超过 sum 并且差值开始增长,由于元素 >=1,prod 增长更快,所以对于固定的起点,满足条件的终点是有限的。 具体地,对于固定起点 L,当我们增加 R 时,S = 从 L 到 R 的和,P = 从 L 到 R 的乘积。 初始时,当 R=L 时,S=a[L],P=a[L],所以相等。 对于 R>L,S 和 P 都增加,但 P 增加得更快,除非 a[R] 很小。 但 a[R] >=1,所以 P 至少乘以 a[R] >=1,而 S 增加 a[R],所以乘数效应。 实际上,对于固定 L,当 R 增加时,P 可以变得比 S 大得多,因此只有少数几个 R 可能满足 P=S。 事实上,对于每个 L,可能的 R 的数量是有限的,因为一旦 P > S,并且由于 a[R] >=1,对于后续的 R,P 增长更快,因此 P 和 S 之间的差距会增加,所以 P=S 可能只在小的 R 时成立。 更精确地说,对于固定 L,设 S_R 和 P_R。 当 R 增加时,P_{R} = P_{R-1} * a[R],S_R = S_{R-1} + a[R]。 如果 P_{R-1} > S_{R-1},那么由于 a[R] >=1,P_R >= P_{R-1} *1 = P_{R-1} > S_{R-1} <= S_R - a[R] <= S_R,但 S_{R-1} <= S_R,所以 P_R > S_{R-1} <= S_R,但 P_R 可能大于或小于 S_R。 例如,如果 P_{R-1} > S_{R-1},那么 P_R = P_{R-1} * a[R] >= P_{R-1} > S_{R-1},而 S_R = S_{R-1} + a[R] <= S_{R-1} + M,但 P_{R-1} * a[R] 可能很大。 但 S_R <= S_{R-1} + M,而 P_R = P_{R-1} * a[R] >= P_{R-1} * 1,所以如果 P_{R-1} > S_{R-1} + M,那么 P_R > S_{R-1} + M >= S_R,因此 P_R > S_R。 类似地,如果 P_{R-1} 远大于 S_{R-1},那么对于所有更大的 R,P 将大于 S。 但边界取决于数值。 在实践中,对于正整数,当元素有界时,对于每个 L,可能满足 P=S 的 R 的数量是常数,或者至少是 O(1)。 例如,如果所有元素 >=1,并且最大值是 M,那么对于固定的 L,当 P 变得太大时,我们可以停止。 具体地,当 P > S 并且 P - S > 0 时,由于 a[R] >=1,P 增长更快,因此一旦 P > S,对于所有后续的 R,P 将保持大于 S,因为 P_{R} / P_{R-1} = a[R] >=1,而 S_R / S_{R-1} <= (S_{R-1} + M)/S_{R-1},但比率没有帮助。 假设在某个 R 处,P > S,并且 P - S = D > 0。 那么对于 R+1,P' = P * a[R+1] >= P * 1 = P S' = S + a[R+1] <= S + M P' - S' >= P - (S + M) = (P - S) - M = D - M 如果 D > M,那么 P' - S' >= D - M > 0,因此 P' > S' 如果 D <= M,那么 P' - S' 可能为负,也可能为正。 但 a[R+1] 可能很大,所以 P' 可能变得更大。 然而,由于 P 是乘法增长,而 S 是加法增长,一旦 P 显著大于 S,它就会保持更大。 但为了安全起见,在算法中,对于每个起始点 L,我们可以将结束点 R 从 L 开始增加,计算当前乘积和和,并检查是否相等,当乘积变得太大时停止,例如,当乘积超过某个阈值,或者当乘积大于和并且差值很大时停止。 由于元素是整数,并且对于大多数数组,这样的子数组是短的,所以我们可以遍历所有 L,对于每个 L,遍历 R,直到乘积变得太大,例如,当 P > S * K 或类似情况,但更简单的方法是,当 P > 2*S 或类似条件时停止,但经验上,对于每个 L,只有 O(1) 个可能的 R,因此总时间复杂度为 O(n)。 在代码中,我们可以使用两个嵌套循环,但内部循环在 P 很大时提前终止。 对于“最长”的问题,我们需要找到所有满足条件的子数组,并记录最大长度。 对于引用[3]中的计数问题,我们统计出现次数。 现在,对于用户,由于用户询问“最长连续子数列”,我将假设是寻找最大长度,使得存在一个子数组,其所有元素的乘积等于所有元素的和。 解题思路: 1. **理解问题**:给定一个正整数数组,找到最长的连续子数组,其中元素的乘积等于元素的和。 2. **关键观察**: - 对于单个元素,乘积总是等于和,因此长度为 1 的子数组总是满足条件。 - 对于更长的子数组,只有当元素较小时才可能,例如包含 1 和 2。 - 由于乘积增长非常快,对于每个起始索引,满足条件的结束索引数量是有限的。 3. **算法选择**: - 我们可以使用滑动窗口或双指针方法,但窗口大小是变化的。 - 更简单的方法是遍历所有可能的起始索引,对于每个起始索引,扩展结束索引,同时维护当前的和与乘积。 - 当乘积变得太大(例如,超过可能的最大和)时,提前终止内部循环。 - 记录满足 prod == sum 的子数组的最大长度。 4. **优化**: - 为了避免溢出,如果数字很大,可以使用带截止条件的 BigInt 或停止。 - 由于对于每个 L,R 的迭代次数是有限的,时间复杂度是 O(n * k),其中 k 是每个 L 的平均迭代次数,对于有界数字,k 是 O(1)。 5. **伪代码**: - 初始化 max_len = 1(因为单个元素总是成立) - 对于 L 从 0 到 n-1: - 设置 curr_sum = a[L], curr_prod = a[L] - 对于 R 从 L+1 到 n-1: - 更新 curr_sum += a[R] - 更新 curr_prod *= a[R] - 如果 curr_prod == curr_sum,则用 (R-L+1) 更新 max_len - 如果 curr_prod > curr_sum 且 curr_prod - curr_sum 很大,但更严格地说,如果 curr_prod > 一个很大的阈值,则中断。简单方法:如果 curr_prod > 2 * curr_sum 或类似,但不可靠。由于数字是整数,当 curr_prod > S_max 时停止,其中 S_max 是最大可能和,但 S_max 可能很大。 - 更好的方法是,当 curr_prod 变得大于当前和并且差距在增加时停止,但为简单起见,我们可以设置一个限制,比如当 curr_prod 超过 10^18 时停止(如果使用 int),或者使用条件:如果 curr_prod > curr_sum * 2 或类似,但经验上,对于大多数情况,内部循环很快结束。 6. **边界情况**:处理空数组或单个元素。 对于蓝桥杯,输入大小可能允许 O(n^2) 如果 n 很小,但 n 可能达到 10^5,所以 O(n^2) 不够好,但使用提前终止,对于随机数据,它可能是 O(n)。 在最坏情况下,如果所有元素都是 1,那么对于每个 L,当 R 增加时,prod 始终为 1,sum 为 (R-L+1),所以 1 == sum 仅当长度为 1,因此对于长度>1,不成立,所以内部循环在 R=L+1 后停止,因为对于 R=L+1,sum=2,prod=1,1<2,并且对于更大的 R,sum 增加,prod 保持为 1,所以 prod < sum,因此永远不会相等,所以内部循环只运行一次或两次。 如果所有元素都是 2,那么对于长度 k,prod=2^k,sum=2k,所以 2^k = 2k 仅当 k=1 或 k=2(4=4),对于 k=3,8>6,等等。所以对于每个 L,当 R=L+1,检查 [L,L+1]:如果 a[L]=2, a[L+1]=2,prod=4, sum=4,成立;对于 R=L+2,prod=8, sum=6, 8>6,并且对于更大的 R,prod 增长更快,因此内部循环在 R=L+2 后停止。 类似地,如果元素是1和2的混合,等等。 所以实际上,对于每个 L,内部循环运行常数次,因此总时间 O(n)。 对于计数问题,类似。 现在,对于用户,我将提供最长子数组的解题思路。 此外,在回答中,我需要使用中文,并且对于数学表达式,使用 $...$ 或 $$...$$。 例如,对于条件:$ \prod_{i=L}^{R} a_i = \sum_{i=L}^{R} a_i $ 在文本中,使用行内格式。 最后,生成相关问题。 所以,回答结构: - 解释问题 - 提供解题思路,包括关键观察和算法 - 可能给出伪代码或代码示例 - 添加引用(由于引用[3]是相关的,在末尾添加[^3]) - 生成相关问题 用户说“蓝桥杯”,所以引用[1]提到“蓝桥杯 2021真题”,因此引用[3]可能是其中的一部分。 在引用[3]中,是“有多少个区间”,但用户询问“最长”,所以我会进行调整。 开始写回答。</think>### 最长连续子数列的解法及算法思路 用户的问题是关于蓝桥杯中“最长连续子数列”的算法解题思路。根据上下文,我推测您可能指的是类似蓝桥杯2021真题中的问题(如引用[3]所述),即寻找一个连续子数组(子数列),使得元素的乘积等于元素的和,并求其最大长度。这类问题通常涉及数组处理,核心是高效地找到满足特定条件($ \prod_{i=L}^{R} a_i = \sum_{i=L}^{R} a_i $)的最长子数组。下面我将一步步解释解题思路,并提供算法实现和优化方法。 #### 问题定义 给定一个正整数数组 $A = (a_1, a_2, \dots, a_n)$,找到最长的连续子数组 $[L, R]$,满足: $$ \prod_{i=L}^{R} a_i = \sum_{i=L}^{R} a_i $$ 其中,$1 \leq n \leq 10^5$(蓝桥杯典型数据范围),数组元素为正整数。 #### 关键观察 1. **基本性质**: - 对于单个元素(长度=1),总有 $a_L = a_L$,因此所有长度为1的子数组都满足条件。 - 对于长度 $k \geq 2$ 的子数组,乘积和和相等的情况较少见,因为乘积增长远快于和(尤其当元素值较大时)。例如: - 如果所有元素 $\geq 2$,则 $k \geq 3$ 时,$\prod a_i \geq 2^k$ 而 $\sum a_i \leq k \cdot \max(A)$,$2^k$ 很快超过 $k \cdot \max(A)$。 - 只有当子数组包含小数字(如1、2或3)时,才可能满足条件,例如 $[1,2,3]$:$1 \times 2 \times 3 = 6 = 1+2+3$。 - 数学上,对于长度 $k>1$,元素必须接近1(如多数为1,少数为2或3),否则乘积会迅速超过和。 2. **算法效率**: - 暴力枚举所有子数组($O(n^2)$ 个子数组)并逐个计算乘积和和($O(n)$ 时间)会导致 $O(n^3)$ 复杂度,无法处理大 $n$(如 $n=10^5$)。 - 优化关键:对于每个起始索引 $L$,满足条件的结束索引 $R$ 数量有限(通常 O(1)),因为一旦乘积显著超过和,后续 $R$ 不可能满足条件(乘积增长更快)。因此,我们可以提前终止内部循环。 #### 解题思路:滑动窗口与提前终止 我们可以使用一个改进的双重循环: - **外层循环**:遍历所有可能的起始索引 $L$。 - **内层循环**:从 $L$ 开始扩展结束索引 $R$,维护当前子数组的和 $S$ 和乘积 $P$。 - 如果 $P = S$,更新最大长度。 - 提前终止条件:当 $P$ 超过 $S$ 且差距增大时(例如,$P > 2 \times S$ 或 $P$ 值过大),停止扩展 $R$(因为后续 $R$ 只会使 $P$ 更大)。 - **复杂度**:由于每个 $L$ 的内层循环平均运行 O(1) 次(基于上述观察),总时间复杂度为 $O(n)$,空间复杂度 $O(1)$。 ##### 伪代码 ```plaintext function find_max_length(A): n = length(A) max_len = 1 # 单个元素总是满足条件 for L from 0 to n-1: curr_sum = A[L] curr_prod = A[L] # 单个元素情况已覆盖,max_len 至少为1 for R from L+1 to n-1: curr_sum += A[R] curr_prod *= A[R] # 检查条件 if curr_prod == curr_sum: max_len = max(max_len, R - L + 1) # 提前终止:如果乘积过大且超过和,则退出内层循环 if curr_prod > curr_sum and (curr_prod - curr_sum > some_threshold or curr_prod > MAX_SAFE_VALUE): break return max_len ``` ##### 优化说明 - **提前终止阈值**:`some_threshold` 可设为经验值(如 $10^6$),或动态判断(如 `curr_prod > 2 * curr_sum`)。在蓝桥杯环境中,元素值通常不大,可直接用 `curr_prod > 10^{18}`(避免整数溢出)。 - **边界处理**:如果数组全为1,则仅长度=1满足条件;如果包含小数字,最大长度可能更高(如 $[1,1,1,2,4]$ 长度=5)。 - **实际代码(Python示例)**: ```python def find_max_length(arr): n = len(arr) max_len = 1 # 最小长度为1 for L in range(n): curr_sum = arr[L] curr_prod = arr[L] for R in range(L + 1, n): curr_sum += arr[R] curr_prod *= arr[R] if curr_prod == curr_sum: max_len = max(max_len, R - L + 1) # 提前终止:如果乘积远大于和(或溢出风险),则停止 if curr_prod > 10**18 or (curr_prod > curr_sum and curr_prod - curr_sum > 10**6): break return max_len ``` 在蓝桥杯中,输入规模 $n \leq 10^5$ 时,此代码可高效运行(平均 O(n))。 #### 相关引用 此问题与蓝桥杯2021真题中的区间计数问题类似,但针对“最长”子数组进行了扩展。原问题要求统计满足乘积等于和的区间数量,而本解法聚焦于最大长度[^3]。 #### 相关问题 基于此问题,我生成了以下相关问题,帮助您深入理解: 1. 如何高效统计数组中所有满足乘积等于和的连续子数组数量? 2. 如果数组包含零或负数,最长连续子数组的算法应如何调整? 3. 在最大子数组和(Kadane算法)中,如何引入乘积约束进行优化? 4. 蓝桥杯中,还有哪些常见子数组问题(如最小权值或和与乘积)?[^1] 如果您有具体输入示例或需要更详细的代码实现(如 C++ 或 Java 版),请提供进一步说明!
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值