题目描述
思路
这里介绍两种写法,一种是过不去的,也是我自己想的,一种是可以过的,官解给的。
首先是过不去的dp做法,会超时。
思路比较简单,区间[l,r]的答案可以从两个状态转移过来,第一个是[l+1,r],通过添加元素nums[l]转移得到;第二个是[l,r-1],通过添加元素nums[r]转移得到。
于是你有下面的状态转移方程
难点在怎么判断nums[l]在区间[l+1,r]离其最近且与nums[l]不等的绝对差呢?
我是这么做的:将区间[l+1,r]的元素取出来进行排序,然后使用nums[l]在有序区间进行二分查找,找到最后一个比nums[l]小的元素和第一个比nums[l]大的元素。然后取最小差值。
但这种做法超时,那么官解怎么写的?官解给了个貌似1e7的跑法(查询1e5 * 数值区间100)。
记录了每个前缀情况下,此时出现的所有元素情况,使用一个长度为101的数组记录。
pre[i][c]表示前i个元素,c出现的个数。
那么,pre[i-1][c] != pre[j][c] ,就说明区间[i,j]中有c出现。
那么,我们让c从1到100依次遍历,如果c1出现且连续地c2也出现(), 那么c2-c1可以尝试刷新一次答案。
代码
dp
class Solution:
def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]:
# dp[l][r] = min(
# min(dp[l][r-1] , r在区间[l,r-1]进行二分查找找到最近的元素),
# min(dp[l+1][r-1] , l在区间[l+1,r]进行二分查找找到最近的元素)
# )
def lower_bound(nums, target) :
l , r = 0 , len(nums)
while l < r :
mid = l + (r-l) // 2
if nums[mid] < target :
l = mid + 1
else :
r = mid
return l
@cache
def dfs(l : int , r : int ) -> int :
if r <= l :
return inf # 非法区间
if r == l + 1 :
return abs(nums[r]-nums[l]) if nums[l] != nums[r] else inf
case1 = inf
# 尝试找到nums[r]在区间[l,r-1]的最近的元素
interval = sorted(nums[l:r])
# nums[r]在里面进行二分查找 找到最近的而且不等于nums[r]的元素
target_idx1 = lower_bound(interval , nums[r]) - 1 # 最后一个小于nums[r]的元素
target_idx2 = lower_bound(interval , nums[r] + 1) # 第一个大于nums[r]的元素
if len(interval) > target_idx1 >= 0 :
case1 = min(case1 , abs(nums[r] - interval[target_idx1]))
if len(interval) > target_idx2 >= 0 :
case1 = min(case1 , abs(nums[r] - interval[target_idx2]))
case1 = min(dfs(l,r-1) , case1 )
case2 = inf
# 尝试找到nums[l]在区间[l+1,r]的最近的元素
interval = sorted(nums[l+1:r+1])
# nums[l]在里面进行二分查找 找到最近的而且不等于nums[r]的元素
target_idx1 = lower_bound(interval , nums[l]) - 1 # 最后一个小于nums[l]的元素
target_idx2 = lower_bound(interval , nums[l] + 1) # 第一个大于nums[l]的元素
if len(interval) > target_idx1 >= 0:
case2 = min(case1 , abs(nums[l] - interval[target_idx1]))
if len(interval) > target_idx2 >= 0:
case2 = min(case1 , abs(nums[l] - interval[target_idx2]))
case2 = min(dfs(l,r-1) , case2 )
return min(case1, case2)
ans = []
for ql , qr in queries :
cur = dfs(ql,qr)
if cur == 0 or cur is inf : cur = -1
ans.append(cur)
return ans
前缀和
class Solution:
def minDifference(self, nums: List[int], queries: List[List[int]]) -> List[int]:
C = 100
pre = [[0] * 101]
for x in nums :
cur = pre[-1][:]
# 注意不能写成cur=pre[-1],因为这种写法没有数组的复制会导致所有修改只改动了pre[0]
cur[x] += 1
pre.append(cur)
ans = []
for l , r in queries:
l += 1 # 修正下标偏移
r += 1 # 修正下标偏移
cur_ans = inf
last = 0
for i in range(1,101) :
if pre[l-1][i] != pre[r][i] : # 检查区间[l,r]是否有c的出现
if last != 0 :
cur_ans = min(cur_ans , i - last)
last = i
if cur_ans is inf :
cur_ans = -1
ans.append(cur_ans)
return ans