题目描述
数组小和的定义如下:
例如,数组s = [1, 3, 5, 2, 4, 6],在s[0]的左边小于或等于s[0]的数的和为0;在s[1]的左边小于或等于s[1]的数的和为1;在s[2]的左边小于或等于s[2]的数的和为1+3=4;在s[3]的左边小于或等于s[3]的数的和为1;
在s[4]的左边小于或等于s[4]的数的和为1+3+2=6;在s[5]的左边小于或等于s[5]的数的和为1+3+5+2+4=15。所以s的小和为0+1+4+1+6+15=27
给定一个数组s,实现函数返回s的小和
[要求]
时间复杂度为O(nlogn)O(nlogn),空间复杂度为O(n)O(n)
输入描述:
第一行有一个整数N。表示数组长度 接下来一行N个整数表示数组内的数
输出描述:
一个整数表示答案
示例1
输入
6 1 3 5 2 4 6
输出
27
思路:归并法。对细分的数组进行求解,再将细分的数组进行 merge(1、计算左数组中比右数组中的小数和 2、从小到大排序)
技巧:
双指针:对于左数组 a1,右数组 a2,计算a1中小于 a2中各个元素的元素之和,利用两个指针i、j,如果a1[i]<=a2[j],则说明 a2中[j,right]的元素都大于等于 a[i],因此 i++,j 不变; 如果a1[i]>a2[j],则 a2[j]小于 a1中[i,middle-1]的所有元素,不可能在 a1[i~middle-1]中找到<=a2[j]的元素,因此 j++;
顺便排序(利用新的数组存放从小到大的数组元素),然后最后再进行替换;
import java.util.*;
public class Main{
public static int[] temp;
public static int[] a;
public static void main(String[] args){
int N;
Scanner in=new Scanner(System.in);
while(in.hasNext()){
N=Integer.parseInt(in.nextLine());
a=new int[N];
temp=new int[N];
for(int i=0;i<N;i++){
a[i]=Integer.parseInt(in.next());
}
System.out.println(helper(0,N-1));
}
}
public static long merge(int left,int middle,int right){
//计算 c,bing并且排序
long c=0;
int k=left;
int i=left,j=middle;
while(i<middle&&j<=right){
if(a[i]<=a[j]){
c+=a[i]*(right-j+1);
temp[k++]=a[i++];
}else{
temp[k++]=a[j++];
}
}
while(i<middle){
temp[k++]=a[i++];
}
while(j<=right){
temp[k++]=a[j++];
}
for(int q=left;q<=right;q++){
a[q]=temp[q];
}
return c;
}
public static long helper(int left,int right){
long r=0;
if(left==right) return 0;
//bug:之前将 left 和 riaght 相邻的情况分开讨论了(但是排序放在了 merge 中,因此没有对相邻的排序),这里 merge 统一对任意长度的进行处理
int middle=(left+right+1)>>1;
r=helper(left,middle-1)+helper(middle,right);
long c=merge(left,middle,right);
r=r+c;
//Arrays.sort(a,left,right+1);
return r;
}
}
自己写的(利用 sort 排序、没有用双指针):
import java.util.*;
public class Main{
public static void main(String[] args){
int N;
int[] a;
Scanner in=new Scanner(System.in);
while(in.hasNext()){
N=Integer.parseInt(in.nextLine());
a=new int[N];
for(int i=0;i<N;i++){
a[i]=Integer.parseInt(in.next());
}
System.out.println(helper(a,0,N-1));
}
}
public static long helper(int[] a,int left,int right){
long r=0;
if(left>=right) return 0;
if(left+1==right){
if(a[left]<=a[right]) r=a[left];
}else{
int middle=(left+right+1)>>1;
r=helper(a,left,middle-1)+helper(a,middle,right);
long c=0;
for(int i=left;i<=middle-1;i++){
int t=middle;
while(t<=right){
if(a[i]<=a[t]){
c+=(right-t+1)*a[i];
break;
}
t++;
}
if(t>right) break;
}
r+=c;
}
Arrays.sort(a,left,right+1);
return r;
}
}