Skip to content

Commit 03403b9

Browse files
authored
feat: add solutions to lcp problem: No.24 (doocs#2292)
No.24.数字游戏
1 parent 297d171 commit 03403b9

File tree

5 files changed

+438
-0
lines changed

5 files changed

+438
-0
lines changed

lcp/LCP 24. 数字游戏/README.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,239 @@
5555

5656
## 解法
5757

58+
### 方法一:优先队列(大小根堆)
59+
60+
我们不妨假设最终的数组元素为 $x, x+1, x+2, \cdots, x+n-1$,那么操作次数为 $|nums[0] - x| + |nums[1] - (x+1)| + \cdots + |nums[n-1] - (x+n-1)|$。我们不妨变换一下式子,得到 $|nums[0] - x| + |nums[1] - 1 - x| + \cdots + |nums[n-1] - (n-1) - x|$。
61+
62+
如果我们将 $nums[i] - i$ 作为第 $i$ 个元素 $nums[i]$,那么上述式子就变成了求 $|nums[0] - x| + |nums[1] - x| + \cdots + |nums[n-1] - x|$ 的最小值。这等价于求:把数组 $nums$ 操作成相同数字的最小操作次数,即把数组 $nums$ 操作成中位数的最小操作数。
63+
64+
我们可以用大小根堆,动态维护前缀数组的中位数。
65+
66+
具体地,我们创建大根堆、小根堆,其中:小根堆 $q1$ 存放较大的一半元素,大根堆 $q2$ 存放较小的一半元素。
67+
68+
添加元素时,先放入小根堆,然后将小根堆对顶元素弹出并放入大根堆(使得大根堆个数多 $1$);若大小根堆元素个数差超过 $1$,则将大根堆元素弹出放入小根堆。过程中,用两个变量 $s1, s2$ 分别记录两个堆的元素和。
69+
70+
取中位数时,若大根堆元素较多,取大根堆堆顶,否则取两堆顶元素和的平均值。求出中位数 $x$ 后,当前最小操作次数为 $s1 - x \times |q1| + x \times |q2| - s2$。注意,我们需要对最小操作次数取模。
71+
72+
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 为数组长度。
73+
74+
<!-- tabs:start -->
75+
76+
```python
77+
class MedianFinder:
78+
def __init__(self):
79+
self.q1 = []
80+
self.q2 = []
81+
self.s1 = 0
82+
self.s2 = 0
83+
84+
def addNum(self, num: int) -> None:
85+
heappush(self.q1, num)
86+
self.s1 += num
87+
num = heappop(self.q1)
88+
heappush(self.q2, -num)
89+
self.s1 -= num
90+
self.s2 += num
91+
if len(self.q2) - len(self.q1) > 1:
92+
num = -heappop(self.q2)
93+
heappush(self.q1, num)
94+
self.s1 += num
95+
self.s2 -= num
96+
97+
def findMedian(self) -> int:
98+
if len(self.q2) > len(self.q1):
99+
return -self.q2[0]
100+
return (self.q1[0] - self.q2[0]) // 2
101+
102+
def cal(self) -> int:
103+
x = self.findMedian()
104+
return (self.s1 - x * len(self.q1) + x * len(self.q2) - self.s2) % (10**9 + 7)
105+
106+
107+
class Solution:
108+
def numsGame(self, nums: List[int]) -> List[int]:
109+
n = len(nums)
110+
ans = [0] * n
111+
finder = MedianFinder()
112+
for i, x in enumerate(nums):
113+
finder.addNum(x - i)
114+
ans[i] = finder.cal()
115+
return ans
116+
```
117+
118+
```java
119+
class MedianFinder {
120+
private PriorityQueue<Integer> q1 = new PriorityQueue<>();
121+
private PriorityQueue<Integer> q2 = new PriorityQueue<>(Collections.reverseOrder());
122+
private final int mod = (int) 1e9 + 7;
123+
private long s1;
124+
private long s2;
125+
126+
public MedianFinder() {
127+
}
128+
129+
public void addNum(int num) {
130+
q1.offer(num);
131+
s1 += num;
132+
num = q1.poll();
133+
q2.offer(num);
134+
s1 -= num;
135+
s2 += num;
136+
if (q2.size() - q1.size() > 1) {
137+
num = q2.poll();
138+
q1.offer(num);
139+
s1 += num;
140+
s2 -= num;
141+
}
142+
}
143+
144+
public long findMedian() {
145+
if (q2.size() > q1.size()) {
146+
return q2.peek();
147+
}
148+
return (q1.peek() + q2.peek()) / 2;
149+
}
150+
151+
public int cal() {
152+
long x = findMedian();
153+
return (int) ((s1 - x * q1.size() + x * q2.size() - s2) % mod);
154+
}
155+
}
156+
157+
class Solution {
158+
public int[] numsGame(int[] nums) {
159+
int n = nums.length;
160+
int[] ans = new int[n];
161+
MedianFinder finder = new MedianFinder();
162+
for (int i = 0; i < n; ++i) {
163+
finder.addNum(nums[i] - i);
164+
ans[i] = finder.cal();
165+
}
166+
return ans;
167+
}
168+
}
169+
```
170+
171+
```cpp
172+
class MedianFinder {
173+
public:
174+
MedianFinder() {
175+
}
176+
177+
void addNum(int num) {
178+
q1.push(num);
179+
s1 += num;
180+
num = q1.top();
181+
q2.push(num);
182+
q1.pop();
183+
s2 += num;
184+
s1 -= num;
185+
if (q2.size() - q1.size() > 1) {
186+
num = q2.top();
187+
q1.push(num);
188+
q2.pop();
189+
s1 += num;
190+
s2 -= num;
191+
}
192+
}
193+
194+
int findMedian() {
195+
if (q2.size() > q1.size()) {
196+
return q2.top();
197+
}
198+
return (q1.top() + q2.top()) / 2;
199+
}
200+
201+
int cal() {
202+
long long x = findMedian();
203+
return (s1 - x * q1.size() + x * q2.size() - s2) % mod;
204+
}
205+
206+
private:
207+
priority_queue<int, vector<int>, greater<int>> q1;
208+
priority_queue<int> q2;
209+
long long s1 = 0;
210+
long long s2 = 0;
211+
const int mod = 1e9 + 7;
212+
};
213+
214+
class Solution {
215+
public:
216+
vector<int> numsGame(vector<int>& nums) {
217+
int n = nums.size();
218+
vector<int> ans(n);
219+
MedianFinder finder;
220+
for (int i = 0; i < n; ++i) {
221+
finder.addNum(nums[i] - i);
222+
ans[i] = finder.cal();
223+
}
224+
return ans;
225+
}
226+
};
227+
```
228+
229+
```go
230+
func numsGame(nums []int) []int {
231+
n := len(nums)
232+
ans := make([]int, n)
233+
finder := newMedianFinder()
234+
for i, x := range nums {
235+
finder.AddNum(x - i)
236+
ans[i] = finder.Cal()
237+
}
238+
return ans
239+
}
240+
241+
type MedianFinder struct {
242+
q1 hp
243+
q2 hp
244+
s1 int
245+
s2 int
246+
}
247+
248+
func newMedianFinder() *MedianFinder {
249+
return &MedianFinder{hp{}, hp{}, 0, 0}
250+
}
251+
252+
func (this *MedianFinder) AddNum(num int) {
253+
heap.Push(&this.q1, num)
254+
this.s1 += num
255+
num = heap.Pop(&this.q1).(int)
256+
heap.Push(&this.q2, -num)
257+
this.s1 -= num
258+
this.s2 += num
259+
if this.q2.Len()-this.q1.Len() > 1 {
260+
num = -heap.Pop(&this.q2).(int)
261+
heap.Push(&this.q1, num)
262+
this.s1 += num
263+
this.s2 -= num
264+
}
265+
}
266+
267+
func (this *MedianFinder) FindMedian() int {
268+
if this.q2.Len() > this.q1.Len() {
269+
return -this.q2.IntSlice[0]
270+
}
271+
return (this.q1.IntSlice[0] - this.q2.IntSlice[0]) / 2
272+
}
273+
274+
func (this *MedianFinder) Cal() int {
275+
x := this.FindMedian()
276+
return (this.s1 - x*this.q1.Len() + x*this.q2.Len() - this.s2) % 1000000007
277+
}
278+
279+
type hp struct{ sort.IntSlice }
280+
281+
func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] }
282+
func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) }
283+
func (h *hp) Pop() any {
284+
a := h.IntSlice
285+
v := a[len(a)-1]
286+
h.IntSlice = a[:len(a)-1]
287+
return v
288+
}
289+
```
290+
291+
<!-- tabs:end -->
292+
58293
<!-- end -->

lcp/LCP 24. 数字游戏/Solution.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
class MedianFinder {
2+
public:
3+
MedianFinder() {
4+
}
5+
6+
void addNum(int num) {
7+
q1.push(num);
8+
s1 += num;
9+
num = q1.top();
10+
q2.push(num);
11+
q1.pop();
12+
s2 += num;
13+
s1 -= num;
14+
if (q2.size() - q1.size() > 1) {
15+
num = q2.top();
16+
q1.push(num);
17+
q2.pop();
18+
s1 += num;
19+
s2 -= num;
20+
}
21+
}
22+
23+
int findMedian() {
24+
if (q2.size() > q1.size()) {
25+
return q2.top();
26+
}
27+
return (q1.top() + q2.top()) / 2;
28+
}
29+
30+
int cal() {
31+
long long x = findMedian();
32+
return (s1 - x * q1.size() + x * q2.size() - s2) % mod;
33+
}
34+
35+
private:
36+
priority_queue<int, vector<int>, greater<int>> q1;
37+
priority_queue<int> q2;
38+
long long s1 = 0;
39+
long long s2 = 0;
40+
const int mod = 1e9 + 7;
41+
};
42+
43+
class Solution {
44+
public:
45+
vector<int> numsGame(vector<int>& nums) {
46+
int n = nums.size();
47+
vector<int> ans(n);
48+
MedianFinder finder;
49+
for (int i = 0; i < n; ++i) {
50+
finder.addNum(nums[i] - i);
51+
ans[i] = finder.cal();
52+
}
53+
return ans;
54+
}
55+
};

lcp/LCP 24. 数字游戏/Solution.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
func numsGame(nums []int) []int {
2+
n := len(nums)
3+
ans := make([]int, n)
4+
finder := newMedianFinder()
5+
for i, x := range nums {
6+
finder.AddNum(x - i)
7+
ans[i] = finder.Cal()
8+
}
9+
return ans
10+
}
11+
12+
type MedianFinder struct {
13+
q1 hp
14+
q2 hp
15+
s1 int
16+
s2 int
17+
}
18+
19+
func newMedianFinder() *MedianFinder {
20+
return &MedianFinder{hp{}, hp{}, 0, 0}
21+
}
22+
23+
func (this *MedianFinder) AddNum(num int) {
24+
heap.Push(&this.q1, num)
25+
this.s1 += num
26+
num = heap.Pop(&this.q1).(int)
27+
heap.Push(&this.q2, -num)
28+
this.s1 -= num
29+
this.s2 += num
30+
if this.q2.Len()-this.q1.Len() > 1 {
31+
num = -heap.Pop(&this.q2).(int)
32+
heap.Push(&this.q1, num)
33+
this.s1 += num
34+
this.s2 -= num
35+
}
36+
}
37+
38+
func (this *MedianFinder) FindMedian() int {
39+
if this.q2.Len() > this.q1.Len() {
40+
return -this.q2.IntSlice[0]
41+
}
42+
return (this.q1.IntSlice[0] - this.q2.IntSlice[0]) / 2
43+
}
44+
45+
func (this *MedianFinder) Cal() int {
46+
x := this.FindMedian()
47+
return (this.s1 - x*this.q1.Len() + x*this.q2.Len() - this.s2) % 1000000007
48+
}
49+
50+
type hp struct{ sort.IntSlice }
51+
52+
func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] }
53+
func (h *hp) Push(v any) { h.IntSlice = append(h.IntSlice, v.(int)) }
54+
func (h *hp) Pop() any {
55+
a := h.IntSlice
56+
v := a[len(a)-1]
57+
h.IntSlice = a[:len(a)-1]
58+
return v
59+
}

0 commit comments

Comments
 (0)