求和的算法(蓝桥杯2022年十三届省赛真题大学C组)

题目如下:

蓝桥杯2022年第十三届省赛真题-求和

时间限制: 2s 内存限制: 320MB 提交: 20564 解决: 4834

题目描述

给定 n 个整数 a1, a2, · · · , an ,求它们两两相乘再相加的和,即 S = a1 · a2 + a1 · a3 + · · · + a1 · an + a2 · a3 + · · · + an-2 · an-1 + an-2 · an + an-1 · an.

输入格式

输入的第一行包含一个整数 n 。 

第二行包含 n 个整数 a1, a2, · · · an。

输出格式

输出一个整数 S,表示所求的和。请使用合适的数据类型进行运算。

样例输入

复制

4
1 3 6 9

样例输出

复制

117

提示

对于 30% 的数据,1 ≤ n ≤ 1000,1 ≤ ai ≤ 100。

对于所有评测用例,1 ≤ n ≤ 200000,1 ≤ ai ≤ 1000。

这是我最开始的解法:

#include <iostream>
using namespace std;

int main(){
	int n;
	cin >> n;
	int All[n];
	for(int i = 0; i < n; i++){
		cin >> All[i];
	}
	//数字已经全部输入数组中 
	 
	 int num;//声明一个数字 来接受一共得到的乘积的 个数 
	 //计算个数
	  num = (n*(n-1))/2;
	  
	 //声明数组来接受乘积 
	 int product[num];
	 
	 int l = 0;   //为传入数据做准备 
	 for(int i = 0; i < n-1; i ++){
	 	
	 	for(int j = i+1; j < n; j++){
	 		
	 		product [l] =  All[i]*All[j];//传入数据 
			 l++;
	 		 
		 }
	 	
	 }
	 
	 //求和
	 int sum = 0;
	 for (int i = 0; i <num; i++){
	 	sum = sum + product[i];
	 }
	 
	 cout << sum << endl;
	 
	 
	 return 0;
}

有很多问题:

(1)在输入数据的时候,用All[n]来接受不太好(VS2022会报错?),它是一个固定大小的数组,而使用 std::vector 替代固定大小的数组,可以提高代码的健壮性和可维护性。

vector<int> All(n);
for (int i = 0; i < n; i++) {
	cin >> All[i];
}

这样就挺好。

(2)题目中要求有大量的数据,所以int类型肯定 不够用,应该使用longlong 类型的来防止可能的溢出。

//使用longlong 防止大量数据可能的溢出
 long long int sum = 0;

(3)不需要显式存储所有的乘积,可以直接在求和过程中进行计算。即在计算乘积的同时直接累加到 sum 中。这样可以减少内存使用并提高性能。

for (int i = 0; i < n - 1; i++) {

	for (int j = i + 1; j < n; j++) {

		sum += All[i] * All[j];
		
	}
}

这是我目前能写出的最强版本:

#include <iostream>
#include <vector>
using namespace std;

int main() {
	int n;
	cin >> n;

	vector<int> All(n);
	for (int i = 0; i < n; i++) {
		cin >> All[i];
	}
	//直接计算乘积并累加
	//使用longlong 防止大量数据可能的溢出
	 long long int sum = 0;
	
	for (int i = 0; i < n - 1; i++) {

		for (int j = i + 1; j < n; j++) {

			sum += All[i] * All[j];
			
		}

	}

	cout << sum << endl;
	return 0;
}

--------------------------------------------------------------------------------------------------------------

(4)上面的算法仍然很慢,时间复杂度为 (O(n^2)),因为有两层嵌套循环。

其实可以有更好的解决方法,只使用一层循环利用 前缀的方法实现。(但是我还没学到)

接下来是引用 作者: 指针原来是套娃的 的题解

     我们观察式子可以发现,可以把每一个数提出来,以a1到a5为例

S=a1*(a2+a3+a4+a5)+a2*(a3+a4+a5)+a3*(a4+a5)+a4*a5

这样很容易想到前缀和,我们可以通过前缀和来得到括号里面的数,这样时间复杂度就降到了O(n)

参考代码:

#include <stdio.h>
 
long long  p[200007];
long long dp[200007];//开到long long 来存放这些数之和
 
int main (){
    int i,j;
    int n;
    long long z=0;//注意开到long long
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%d",&p[i]);
        if(i==0)dp[i]=p[i];
        else   dp[i]=dp[i-1]+p[i];//前缀和数组
    }
    for(i=0;i<n;i++){
        z+=p[i]*(dp[n-1]-dp[i]);//dp[n-1]-dp[i]即为()里的数
    } 
    printf("%lld",z);
     
    return 0;
}

最后把链接放在这:前缀和、差分数列的计算方法

我会继续学习!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值