LCP 24. 数字游戏
思路:当求解到第i个元素时,即需要区间[0,i]满足x、x+1、……、x+i。那么总的移动次数就是abs(nums[0]-x)+abs(nums[1]-(x+1))+……+abs(nums[i]-(x+i)),即abs(nums[0]-x)+abs((nums[1]-1)-x)+……+abs((nums[i]-i)-x)。这就化简为x到这些点之间的距离之和,要使距离之和最小,就是把x放在中位数上。
在这里考虑到长度可能到达10^5,我们不能用两层循环。那就用两个优先队列维护一个小顶堆和一个大顶堆。这样时间复杂度为0(nlogn)。细节看注释
class Solution {
public:
const int mod=1e9+7;
vector<int> numsGame(vector<int>& nums) {
vector<int> v;
//大顶堆维护左半段小的数
priority_queue<int> left;
//小顶堆维护右半段大的数
priority_queue<int,vector<int>,greater<int>> right;
//两边数的和
long long sum_left=0,sum_right=0;
for(int i=0;i<nums.size();i++){
//得到新的点
int tmp=nums[i]-i;
//当前点为偶数,说明前面已经有偶数个点被分配
if(i%2==0){
//先放进右半段更新
right.push(tmp);
sum_right+=tmp;
//获得新的右半段最小值
tmp=right.top();
right.pop();
sum_right-=tmp;
//放入左半段进行更新
left.push(tmp);
sum_left+=tmp;
//获得左半段最大值
tmp=left.top();
cout<<tmp<<endl;
sum_left-=tmp;
v.push_back((sum_right-sum_left)%mod);
//这里左半段没有弹出,因为这个多出来的数先放在左半段
sum_left+=tmp;
}else{
//当前点为奇数,说明前面已经有奇数个点被分配,左半段多一个点
//所以先将新的点放进左半段进行更新
left.push(tmp);
sum_left+=tmp;
//获得左半段最大值
tmp=left.top();
sum_left-=tmp;
//这里需要弹出
left.pop();
//直接放进右半段,这样两边数量相等
right.push(tmp);
sum_right+=tmp;
v.push_back((sum_right-sum_left)%mod);
}
}
return v;
}
};