题目描述:
现定义数组单调和为所有元素i的f(i)值之和。这里的f(i)函数定义为元素i左边(不包括其自身)小于等于它的数字之和。请设计一个高效算法,计算数组的单调和。
给定一个数组A同时给定数组的大小n,请返回数组的单调和。保证数组大小小于等于500,同时保证单调和不会超过int范围。
测试样例:
[1,3,5,2,4,6],6
返回:27
思路:暴力解法是采用双层循环,时间复杂度是o(n^2),空间复杂度是o(1)。
为降低时间复杂度,我们可以借助归并排序的思想:在每次递归过程中,我们只需将前半部分的元素和后半部分的元素相比较,如果前面的数值较小,则使单调和增加该值乘以后半部分中与该较小值相比较的那个数以后(包过自己)数值的个数即可。以数组[1,3,5,2,4,6]为例,如图所示,当递归到最底层的时候开始计算单调和sum(初始化为0),步骤如下:
第一步: 比较子数组[1]与[3],因为1<3,所以单调和加1,即sum=0+1=1。
第二步: 比较子数组[2]与[4],因为2<4,所以单调和加2,即sum=1+2=3。
第三步: 比较子数组[1,3]与[5],因为1<5,所以单调和加1,即sum=3+1=4,因为3<5,所以单调和加3,即sum=4+3=7。
第四步: 比较子数组[2,4]与[6],因为2<6,所以单调和加2,即sum=7+2=9,因为4<6,所以单调和加4,即sum=9+4=13。
第五步: 比较子数组[1,3,5]与[2,4,6],因为1<2,所以单调和加1*3,即sum=13+3=16,因为2<3<6,所以单调和加3*2,即sum=16+6=22,因为4<5<6,所以单调和加5*1,即sum=22+5=27。
相关代码如下:
class MonoSum {
public:
int merge(vector<int> &source,vector<int> &temp,int start,int end){
if(start==end){
temp[start]=source[start];
return 0;
}
int mid=(start+end)/2;
int id=start;
int sum=0;
int left=merge(temp,source,start,mid);
int right=merge(temp,source,mid+1,end);
int i=start,j=mid+1,k=start;
while(i!=mid+1&& j!=end+1){
if(source[i] <= source[j]){
sum+=source[i]*(end-j+1);
temp[id++]=source[i++];
}
else
temp[id++]=source[j++];
}
while(i!=mid+1)
temp[id++]=source[i++];
while(j!=end+1)
temp[id++]=source[j++];
return sum+left+right;
}
int calcMonoSum(vector<int> A, int n) {
// write code here
vector<int> temp(A);
return merge(A,temp,0,n-1);
}
};