题目如下:
蓝桥杯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;
}
最后把链接放在这:前缀和、差分数列的计算方法
我会继续学习!