前缀和问题

前缀和问题

时间: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值