🔗 🟢 1103. Distribute Candies to People 1187

tags: Weekly Contest 143 模擬(Simulation) 數學(Math)

題意

給定兩個整數 candiescandies 和 n=num_peoplen = num\_people,表示糖果數量和人數。依照以下規則分配糖果:

  • 給第一個人 11 顆糖果,第二個人 22 顆,依此類推,直到第 nn 個人得到 nn 顆糖果。
  • 然後,回到第一個人,給第一個人 n+1n + 1 顆糖果,第二個人 n+2n + 2 顆,依此類推,直到第 nn 個人得到 2n2n 顆糖果。
  • 重複上述過程,直到糖果分配完畢。如果糖果不夠,則剩下的糖果都給最後一個人。

請求出每個人最後得到的糖果數量。

約束條件

  • 1≤candies≤1091 \leq \text{candies} \leq 10^9
  • 1≤n≤10001 \leq n \leq 1000

思路:模擬(Simulation) / 數學(Math)

雖然 candies\text{candies} 的數量可以到 10910^9,但是分配糖果的次數是 O(candies)\mathcal{O}(\sqrt{\text{candies}}) 級別的,因此可以直接模擬分配的過程。此外,也可以根據數學公式計算出每個人最後得到的糖果數量。。

方法一:模擬(Simulation)

按照題意模擬每次的分配過程,直到糖果分配完畢。

具體步驟如下:

  1. 初始化一個長度為 nn 的陣列 ansans,每個元素初始化為 00。
  2. 維護一個變數 ii 表示當前是第幾次分配糖果,初始化為 00。
  3. 使用 while 循環,不斷分配糖果,直到糖果數量 candies 為 0。
  4. 在第 ii 次分配時,將 i+1i + 1 顆糖果分配給第 i mod ni \bmod n 個人。若剩餘糖果數量 candies<i+1candies < i + 1,則分配剩餘的糖果,並結束分配。
  5. 減少 candiescandies 的數量,並增加 ii 。
  6. 返回最終的糖果分配陣列 ansans。

複雜度分析

  • 時間複雜度:O(n+candies)\mathcal{O}(n + \sqrt{candies}),其中 candiescandies 表示糖果數量。
    • 初始化答案陣列的時間複雜度為 O(n)\mathcal{O}(n)。
    • 共需分配 O(candies)\mathcal{O}(\sqrt{candies}) 次糖果,每次分配的時間複雜度為 O(1)\mathcal{O}(1)。具體計算見方法二。
  • 空間複雜度:O(n)\mathcal{O}(n)。
1
2
3
4
5
6
7
8
9
10
class Solution:
def distributeCandies(self, candies: int, num_people: int) -> List[int]:
n = num_people
ans = [0] * n
i = 0
while candies > 0: # 第 i 次分配 i + 1 顆糖果給第 i % n 個人
ans[i % n] += min(candies, i + 1)
candies -= i + 1
i += 1
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> distributeCandies(int candies, int num_people) {
int n = num_people;
vector<int> ans(n, 0);
int i = 0;
while (candies > 0) {
ans[i % n] += min(candies, i + 1);
candies -= i + 1;
i++;
}
return ans;
}
};

方法二:數學(Math)

在方法一計算時間複雜度時,提及總共分配的次數為 O(candies)\mathcal{O}(\sqrt{candies}),這是因為分配的次數可以被計算出來。令可以完整分配的次數為 mm,推導流程如下:

  • 在第 mm 次分配時,總共分配了 1+2+⋯+m=m(m+1)21 + 2 + \cdots + m = \frac{m(m+1)}{2} 顆糖果。故 m(m+1)2≤candies\frac{m(m+1)}{2} \leq candies。
  • 得到 m2+m−2⋅candies≤0m^2 + m - 2 \cdot candies \leq 0,解這個一元二次不等式,得到 m≤−1+8⋅candies+12m \leq \frac{-1 + \sqrt{8 \cdot candies + 1}}{2}。
  • 故可以完整分配的次數 m=⌊−1+8⋅candies+12⌋m = \left\lfloor \frac{-1 + \sqrt{8 \cdot candies + 1}}{2} \right\rfloor。

根據可以完整分配的次數 mm,可以得出可以完整分配的輪數 qq 及剩下的次數 rr,分別為 q=⌊mn⌋q = \lfloor \frac{m}{n} \rfloor 和 r=m mod nr = m \bmod n。

在完整分配的 qq 輪中,對於第 11 個人到第 nn 個人,可以計算每個人最後得到的糖果數量:

  • 第 11 個人在前 qq 輪中分配到的糖果數量分別為 1,1+n,1+2n,…,1+(q−1)n1, 1 + n, 1 + 2n, \ldots, 1 + (q-1)n,總共為 q⋅(q−1)2⋅n+1⋅q\frac{q \cdot (q - 1)}{2} \cdot n + 1 \cdot q。
  • 第 22 個人在前 qq 輪中分配到的糖果數量分別為 2,2+n,2+2n,…,2+(q−1)n2, 2 + n, 2 + 2n, \ldots, 2 + (q-1)n,總共為 q⋅(q−1)2⋅n+2⋅q\frac{q \cdot (q - 1)}{2} \cdot n + 2 \cdot q。
  • 第 ii 個人在前 qq 輪中分配到的糖果數量分別為 i,i+n,i+2n,…,i+(q−1)ni, i + n, i + 2n, \ldots, i + (q-1)n,總共為 q⋅(q−1)2⋅n+i⋅q\frac{q \cdot (q - 1)}{2} \cdot n + i \cdot q。

最後考慮剩餘 rr 次分配的情況:

  • 對於第 11 個人,若在第 q+1q+1 輪中也能被完整分配,則其在第 q+1q+1 輪中分配到的糖果數量為 q⋅n+1q \cdot n + 1。
  • 對於第 ii 個人,若在第 q+1q+1 輪中也能被完整分配,則其在第 q+1q+1 輪中分配到的糖果數量為 q⋅n+iq \cdot n + i。
  • 若不能完整分配,則其在第 q+1q+1 輪中分配到的糖果數量為 candies−m⋅(m+1)2candies - \frac{m \cdot (m + 1)}{2}。

具體步驟如下:

  1. 計算可以完整分配的次數 mm,以及可以完整分配的輪數 qq 和 剩下的次數 rr。
  2. 初始化一個長度為 nn 的陣列 ansans,每個元素初始化為 00。
  3. 遍歷每個人,對於第 i+1i+1 個人:
    • 若 i<ri < r,則每人應得糖果為 q⋅(q−1)2⋅n+(i+1)⋅q+(q⋅n+i+1)\frac{q \cdot (q - 1)}{2} \cdot n + (i + 1) \cdot q + (q \cdot n + i + 1)。
    • 若 i=ri = r,則每人應得糖果為 q⋅(q−1)2⋅n+(i+1)⋅q+(candies−m⋅(m+1)2)\frac{q \cdot (q - 1)}{2} \cdot n + (i + 1) \cdot q + (candies - \frac{m \cdot (m + 1)}{2})。
    • 若 i>ri > r,則每人應得糖果為 q⋅(q−1)2⋅n+(i+1)⋅q\frac{q \cdot (q - 1)}{2} \cdot n + (i + 1) \cdot q。
  4. 返回最終的糖果分配陣列 ansans。

複雜度分析

  • 時間複雜度:O(n)\mathcal{O}(n),其中 nn 表示人數。
  • 空間複雜度:O(n)\mathcal{O}(n)。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def distributeCandies(self, candies: int, num_people: int) -> List[int]:
n = num_people
m = int((-1 + sqrt(8 * candies + 1)) / 2) # 可以分配 m 次
q, r = divmod(m, n) # 可以完整分配 q 輪,剩下 r 個
ans = [0] * n
for i in range(n):
ans[i] = q * (q - 1) // 2 * n + q * (i + 1)
if i < r:
ans[i] += q * n + i + 1
elif i == r:
ans[i] += candies - m * (m + 1) // 2
return ans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
vector<int> distributeCandies(int candies, int num_people) {
int n = num_people;
int m = (-1 + sqrt(8.0 * candies + 1.0)) / 2;
int q = m / n, r = m % n;
vector<int> ans(n, 0);
for (int i = 0; i < n; i++) {
ans[i] = q * (q - 1) / 2 * n + q * (i + 1);
if (i < r) ans[i] += q * n + i + 1;
else if (i == r) ans[i] += candies - m * (m + 1) / 2;
}
return ans;
}
};

參考資料


寫在最後

Cover photo is created by @たろたろ, thanks for their work!