题目详情:
小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N
个计数器,计数器编号为 0 ~ N-1
。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums
。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。
主办方请小扣回答出一个长度为 N
的数组,第i
个元素(0 <= i < N)
表示将 0~i
号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i)
的最小操作数。回答正确方可进入秋日市集。
由于答案可能很大,请将每个最小操作数对 1,000,000,007
取余。
示例 1:
输入:nums = [3,4,5,1,6,7]
输出:[0,0,0,5,6,7]
解释:
i = 0,[3] 无需操作
i = 1,[3,4] 无需操作;
i = 2,[3,4,5] 无需操作;
i = 3,将 [3,4,5,1] 操作成 [3,4,5,6], 最少 5 次操作;
i = 4,将 [3,4,5,1,6] 操作成 [3,4,5,6,7], 最少 6 次操作;
i = 5,将 [3,4,5,1,6,7] 操作成 [3,4,5,6,7,8],最少 7 次操作;
返回 [0,0,0,5,6,7]。
示例 2:
输入:nums = [1,2,3,4,5]
输出:[0,0,0,0,0]
解释:对于任意计数器编号 i 都无需操作。
示例 3:
输入:nums = [1,1,1,2,3,4]
输出:[0,1,2,3,3,3]
解释:
i = 0,无需操作;
i = 1,将 [1,1] 操作成 [1,2] 或 [0,1] 最少 1 次操作;
i = 2,将 [1,1,1] 操作成 [1,2,3] 或 [0,1,2],最少 2 次操作;
i = 3,将 [1,1,1,2] 操作成 [1,2,3,4] 或 [0,1,2,3],最少 3 次操作;
i = 4,将 [1,1,1,2,3] 操作成 [-1,0,1,2,3],最少 3 次操作;
i = 5,将 [1,1,1,2,3,4] 操作成 [-1,0,1,2,3,4],最少 3 次操作;
返回 [0,1,2,3,3,3]。
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^3
解题思路:
题目难点:
1)、如何将计算的操作次数,转化为每个新数据与中位数的距离之间的关系
;(分析题目要做的事情
)
新数据: nums[i] - i
; //nums[a]+1 == nums[a+1],(0 <= a < i)
即 斜率 为: 1
;
2)、如何选择数据结构,使插入一条数据
+ 求中位数
所耗费 时间最短;(vector , queue, priority_queue
) ;
优化后的解题思路:
1)、根据条件:nums[a]+1 == nums[a+1],(0 <= a < i)
,生成新的数据 nums[i] - i
; 求解为题转化为求 转化为每个新数据与中位数的距离之间的关系
;
2)、q0
:小顶堆是存储 中位数到右侧的数据;
1: 当 访问的数据的个数为奇数
的时候,则表示 q0
的数据个数要增加一个
;
增加的时候要要讨论 新进的nums[i] - i
与 q0.top()
的大小比较 ;
if (q0.top() > tmp)
{
q1.push(tmp) ;
tmp2 = q1.top() ;
q1.pop() ;
q0.push(tmp2) ;
preNum += tmp2 ;
}
else{
preNum += tmp ;
q0.push(tmp) ;
}
2: 当 访问的数据的个数为偶数
的时候 ; q0.size()
大小不变,需要讨论,是否需要数据的替换;
{
if (q0.top() < tmp){
preNum -= q0.top() ;
q1.push(q0.top());
q0.pop() ;
q0.push(tmp) ;
preNum += tmp ;
}else{
q1.push(tmp) ;
}
}
3)、preNum
: 为了减少计算sum(q0)
的耗时做的优化;
4)、midmin
:计算当前i
下;需要的操作次数;
// midmin :计算需要 操作的次数;
midmin = preNum - q0.size() * tmp ;
midmin += tmp * q1.size() - Cnum + preNum ;
ret.push_back(midmin % 1000000007) ;
代码实现1(超时:数据结构选择不当,插入耗时过大):
class Solution {
public:
int localVector(vector<int>& lnum , int val)
{
int fir = 0 , sec = lnum.size() - 1 , mid ;
while (fir <= sec)
{
mid = (fir + sec) >> 1 ;
if (lnum[mid] == val) return mid + 1 ;
else if (lnum[mid] > val) sec = mid - 1 ;
else fir = mid + 1 ;
}
return fir ;
}
vector<int> numsGame(vector<int>& nums) {
int i , j ;
vector<int> ret ;
long long midmin = 0 , loc = 0 , tmp ;
vector<int> cha ;
for(i = 0 ; i < nums.size() ; i ++)
{
tmp = nums[i] - i ;
loc = localVector(cha , tmp) ;
cha.insert(cha.begin() + loc , tmp) ;
tmp = cha[cha.size() >> 1] ;
midmin = 0 ;
for(j = 0 ; j < cha.size() ; j ++) midmin += (cha[j] > tmp) ? (cha[j] - tmp) : (tmp - cha[j]) ;
ret.push_back(midmin % 1000000007) ;
}
return ret ;
}
};
class Solution {
public:
int localVector(vector<int>& lnum , int val)
{
int fir = 0 , sec = lnum.size() - 1 , mid ;
while (fir <= sec)
{
mid = (fir + sec) >> 1 ;
if (lnum[mid] == val) return mid + 1 ;
else if (lnum[mid] > val) sec = mid - 1 ;
else fir = mid + 1 ;
}
return fir ;
}
vector<int> numsGame(vector<int>& nums) {
int i , j ;
vector<int> ret ;
long long midmin = 0 , loc = 0 , tmp , midloc ;
vector<int> cha ;
cha.push_back(nums[0]) ;
ret.push_back(0) ;
long long Cnum = nums[0] , preNum = nums[0] ;
for(i = 1 ; i < nums.size() ; i ++)
{
tmp = nums[i] - i ;
Cnum += tmp ;
loc = localVector(cha , tmp) ;
cha.insert(cha.begin() + loc , tmp) ;
midloc = (cha.size() - 1) >> 1 ;
//cout << midloc << '\t' << loc << endl ;
if (cha.size() & 1)
{
if(loc <= midloc) preNum += cha[loc] ;
else preNum += cha[midloc] ;
}
else
{
if (loc <= midloc){
preNum -= cha[midloc + 1] ;
preNum += cha[loc] ;
}
}
tmp = cha[midloc] ;
midmin = 0 ;
midmin = tmp * midloc + tmp - preNum ;
midmin += Cnum - preNum - tmp * (cha.size() - 1 - midloc) ;
ret.push_back(midmin % 1000000007) ;
}
return ret ;
}
};
代码实现2:
class Solution {
public:
vector<int> numsGame(vector<int>& nums) {
int i , j ;
vector<int> ret ; // 存储返回的结果
priority_queue<int,vector<int>,greater<int>> q0;//小顶堆
priority_queue <int,vector<int>,less<int>> q1; // 大顶堆
ret.push_back(0) ;
q0.push(nums[0]) ;
long long tmp , tmp2 , midmin;
long long Cnum = nums[0] , preNum = nums[0] ;
//ret里每一项的值
for(i = 1 ; i < nums.size() ; i ++)
{
// nums[i] - i 将问题转化为求中位数
tmp = nums[i] - i ;
Cnum += tmp ;
//当访问的数据为奇数个时,q0的个数需要增加一个;
if ((i + 1) & 1)
{
if (q0.top() > tmp)
{
q1.push(tmp) ;
tmp2 = q1.top() ;
q1.pop() ;
q0.push(tmp2) ;
preNum += tmp2 ;
}
else{
preNum += tmp ;
q0.push(tmp) ;
}
}
//当访问的数据为偶数个时,q0数据个数保持不变 ;
else
{
if (q0.top() < tmp){
preNum -= q0.top() ;
q1.push(q0.top());
q0.pop() ;
q0.push(tmp) ;
preNum += tmp ;
}else{
q1.push(tmp) ;
}
}
tmp = q0.top() ;
midmin = 0 ;
// midmin :计算需要 操作的次数;
midmin = preNum - q0.size() * tmp ;
midmin += tmp * q1.size() - Cnum + preNum ;
ret.push_back(midmin % 1000000007) ;
}
return ret ;
}
};
复杂度计算:
空间复杂度 O(n)
;