import java.util.*; public class BiggestSum3260 { public static void main(String[] args) { Scanner s = new Scanner(System.in); int t = s.nextInt();//样例个数// long []a = new long [(int)1e5*2+5]; for(int i = 0; i < t; i ++){ int n = s.nextInt(); int k = s.nextInt(); for(int j = 1; j <= n ; j ++){ a[j] = s.nextLong(); } Arrays.sort(a,1,n+1);//宝石价值升序排序// for(int j = 1; j <= n; j ++){ a[j] += a[j-1];//a[j]表示前j项的和// } long sum; long max = 0; for(int left = 0,right; left <= 2*k ; left+=2){ right = n-(k-left/2); sum = a[right] - a[left]; max= Math.max(max,sum); } System.out.println(max); } } }
又是被算法题折磨的一天。
前缀和总结
昨天学习了前缀和知识,一直似懂非懂。练了两道例题才稍有理解。所谓的前缀和,就是经过预处理(时间复杂度为O(n))后,能使m次查询(l,r)区间上元素之和的时间复杂度每次均为O(1),总复杂度为O(n+m);而普通暴力算法下的时间复杂度为O(n*m)。也就是说它降低了区间多次求和的时间复杂度。因此,在遇到较大的n和m时,我们可以使用前缀和来优化。
前缀和模版
前缀和的模版就是创建一个数组sum[],使得sum[i]等于数据数组a[]的前i项和,(l,r)区间之和就等于sum[r]-sum[l-1]。sum数组本身的递推为:sum[i] +=sum[i-1] + a[i-1](当前项=前一项+差分项)。考虑到数组越界的可能,我们在给sum数组赋值时索引从1开始,到n结束。
例题分析
下面说说本题。作为一名刚接触了一点基础算法的大一鼠鼠,我的第一反应是利用贪心,比较两个最小值与一个最大值谁更小,从局部最优推至全局最优。但是,运行后在所给样例中的倒数第二个发生错误,按照贪心的思路应该是先取前两个最小值,再取最大值,最后结果为40。然而,连续去除两次最大值能得到更优的结果46,尽管它并没有做到局部最优。所以改变思路使用暴力枚举出所有结果取最大值。参考了一下题解的思路,发现宝石的价值其实就是去头去尾后,中间一段区间宝石的价值和(如下图),顺理成章地使用前缀和算法,求出区间长度,比较后得到结果。