前缀和问题
时间:2021年5月31日
本次刷题记录一下前缀和问题的暴力解法,以及优化后的递推解法。
问题归类:前缀和,递推
题目描述:
链接:https://ac.nowcoder.com/acm/contest/16520/A
来源:牛客网
最近,AllenAllen 教授研发了一种新的比赛机制,这种比赛机制是 ACPCACPC (Asia ; Collegiate ; Programming ; Contest)(AsiaCollegiateProgrammingContest) 赛制的扩展,简称 AUPCAUPC (Asia ; University ; Programming ; Contest)(AsiaUniversityProgrammingContest)。参赛者为个人参赛(即每人一台电脑),且成绩分为两部分,过题数和罚时。每道题目的罚时 s 与比赛已经开始的时间(按分钟计) a 以及该题错误的提交次数(不包括编译错误) b 有关,即s=a+b×20。
另外,新赛制规定,每位参赛者必须按一定的顺序循环做题,直到解出全部题目或比赛结束。在比赛开始之前,每位参赛者可以任意选择第一个要做的题目和方向:正序或者逆序,但是中途不能跳题或者改变方向。例如,现在有 A,B,C,D,EA,B,C,D,E 五道题目,参赛者可以选择第一个要做的题目 CC,然后按正序 C,D,E,A,BC,D,E,A,B 的顺序做题,或者按逆序 C,B,A,E,DC,B,A,E,D 的顺序做题,而 C,D,B,A,EC,D,B,A,E 或 C,E,A,B,DC,E,A,B,D 都是不被允许的。
AliceAlice 水平非常高,她总是可以做出所有的题目。现在她想知道,如果已知解决每道题需要花费的时间,那么解决 nn 道题的最少罚时是多少。我们认为比赛时间足够 AliceAlice 解决所有题目,在比赛开始的瞬间 AliceAlice 就开始做题并且所有题目都是一次提交便成功通过。
输入描述:
第一行一个整数 T(1≤T≤5×105),表示测试用例的数量。
对于每组测试用例,第一行一个整数 n (1≤n≤5×105),表示题目的数量。
接下来一行 n 个整数,第 i (1≤i≤n) 个整数 A (1≤A≤5×105),表示解决第 i 道题需要花费的时间(按分钟计)。
对于全部测试用例,保证 ∑n≤5×105。
输出描述:
对于每组测试用例,输出一行一个整数表示答案。
输入:
2
5
2 1 5 3 4
10
5 9 3 7 8 1 10 4 6 2
输出:
36
277
说明:
在第一组测试用例中,AliceAlice 按题目编号2,1,5,4,32,1,5,4,3 的顺序做题,总罚时最少,为 1+3+7+10+15 = 361+3+7+10+15 = 36。
在第二组测试用例中,总罚时最少为 5+7+13+17+27+28+36+43+46+55 = 277。
题目分析:
题目虽然很长但是很多信息其实是没有用的,有用的信息就只有“每道题目的罚时 s 与比赛已经开始的时间(按分钟计) a 以及该题错误的提交次数(不包括编译错误) b 有关,即s=a+b×20。”并且b = 0,按顺序加和,然后结果求的是总罚时,由此可以看出是前缀和问题。
暴力参考代码(严重超时,不推荐,反面教材):
#include<bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
long long n;
long long res = 0;
while (T--) {
cin >> n;
long long a[n];
for (long long i = 0; i < n; i++) {
cin >> a[i];
}
long long res1 = 100000000, res2 = 100000000;
for (long long i = 0; i < n; i++) {
long long count1 = i;
long long sum = 0;
long long time1 = 0;
while (1) {
sum += a[count1];
time1 += sum;
count1 = (count1 + 1) % n;
if (count1 == i) {
break;
}
}
res1 = min(time1, res1);
}
for (long long i = 0; i < n; i++) {
long long count2 = i;
long long time2 = 0;
long long sum = 0;
while (1) {
sum += a[count2];
time2 += sum;
if (count2 == 0) {
for (long long j = n - 1; j > i; j--) {
sum += a[j];
time2 += sum;
}
break;
}
count2 = (count2 - 1) % n;
}
res2 = min(res2, time2);
}
res = (res1 - res2) > 0? res2 : res1;
cout << res << endl;
}
return 0;
}
采用递归公式:
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 500005
#define INF 123456789000000000
ll T, n, sum, isum, risum, a[maxn], ans;
int main() {
scanf("%lld", &T);
while (T--) {
scanf("%lld", &n);
sum = isum = risum = 0;
ans = INF;
for (ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
sum += a[i]; isum += i * a[i];
risum += (n - i + 1) * a[i];
}
for (ll i = n; i >= 1; i--) {
isum += sum - n * a[i];
ans = min(ans, isum);
}
for (ll i = 1; i <= n; i++) {
risum += sum - n * a[i];
ans = min(ans, risum);
}
printf("%lld\n", ans);
}
return 0;
}