CSES 🎯 CSES-1665 Coding Company
🔗 CSES-1665 Coding Company
Problem Statement
題目簡述
有 位工程師,每人有一個技術能力值 。需要將他們分成若干個團隊。
每個團隊的懲罰值定義為該團隊中能力值最高與最低成員的差值,即 ;若團隊只有 人,懲罰值為 。
求有多少種分團隊方式,使得所有團隊的懲罰值總和不超過 。答案需對 取模。
Constraints
約束條件
思路:動態規劃 (Open and Close Interval Trick)
這題是典型的「排序後按貢獻計算區間端點」DP。難點不在於某個團隊的懲罰值怎麼算,而在於:分組方式太多,不能真的把每個團隊都枚舉出來。
先看直接做法。若枚舉每一種分組,再逐一計算所有團隊的懲罰值,分支數會隨人數快速爆炸,對 完全不可行。因此需要換一種統計方式。
1. 轉換貢獻計算方式
不妨先把能力值排序。對某個團隊,假設其中成員能力值為
,它的懲罰值是最大值減最小值:
把這個差值拆成相鄰差值之和:
排序後,每一段相鄰能力值差,只會貢獻給那些「已經選了團隊最小值,但還沒選到團隊最大值」的團隊。換句話說,當我們從小到大掃描時,只要知道目前有多少個尚未關閉的團隊,就知道下一段差值會被加幾次。
這就是 Open and Close Interval Trick:把一個團隊看成排序序列上的一段開閉過程。遇到團隊最小值時開啟,遇到團隊最大值時關閉;中間的相鄰差值,都會被這個團隊跨過並貢獻一次。
只要一題的代價長得像「每組最大值減最小值」,並且元素可以排序,就可以優先想:把最大最小差拆成相鄰差值,再用目前開啟的組數計算貢獻。
2. 動態規劃狀態定義
掃描到某個位置時,真正需要保留的資訊只有兩件事:
- 目前還有多少個團隊已開啟但未關閉。
- 目前累積的懲罰值總和是多少。
因此先寫出完整狀態:
表示處理完排序後前 位工程師後,目前有 個團隊已開啟但尚未關閉,且累積懲罰值總和為 的分組方案數。
其中:
- 表示已處理的人數。
- 表示目前開啟中的團隊數。
- 表示目前累積的懲罰值總和,且只需要考慮 。
開啟團隊數還可以進一步限制。任何時刻,開啟中的團隊必須在之後被某個工程師關閉,所以它不能超過剩餘未處理的人數;同時它也不可能超過已處理的人數。因此只需要考慮:
這能把開啟團隊數這一維從 壓到 。
初始狀態為:
一開始還沒有處理任何工程師,也沒有開啟中的團隊,累積懲罰值為 ,這是一種合法空方案。
最後答案要求所有團隊都已關閉,所以是:
也就是處理完所有工程師後,開啟團隊數為 ,且總懲罰值不超過 的所有方案數。
由於第 層只會從第 層轉移而來,實作時用滾動陣列即可,不需要保留完整三維 DP。也就是把目前層寫成 ,下一層寫成 ,含義仍然是「開啟團隊數為 、累積代價為 的方案數」。
3. 狀態轉移:Open and Close Interval Trick
處理下一位工程師前,先看目前能力值與前一個能力值的差。若現在有 個開啟中的團隊,這段差值會被這 個團隊全部跨過,所以累積代價需要增加:
如果增加後已經超過上限,這個狀態就不能繼續轉移。
為了把轉移式寫清楚,令目前狀態為 ,表示處理到上一個位置後,有 個團隊仍然開啟,累積懲罰值為 的方案數。
處理目前位置時,先加入相鄰差值帶來的貢獻。設
若 ,這個狀態無效;否則把方案轉移到下一層狀態 。
接下來考慮目前工程師在某個團隊中的角色,有四種情況:
-
開啟一個新組 ():
- 目前工程師作為某個團隊的最小值。
- 沒有選擇哪個舊團隊的問題,所以係數是 。
-
關閉一個舊組 ():
- 目前工程師作為某個已開啟團隊的最大值。
- 可以選擇 個開啟團隊中的任意一個關閉,所以係數是 。
-
加入一個舊組 ():
- 目前工程師既不是最小值,也不是最大值,只是放入某個已開啟團隊中。
- 有 個團隊可選,所以係數是 。
-
單獨自成一組 ():
- 目前工程師同時是最小值和最大值。
- 相當於開啟後立刻關閉,係數是 。
其中第 3、4 種都不改變開啟團隊數,可以合併成:
這也是程式裡同一層狀態轉移乘上 的來源。
整理成完整轉移式,就是:
實作時再檢查 是否超過開啟組數上界,以及 時才允許關閉舊組。
每位工程師在排序後只會被處理一次,而它對分組的角色只可能是開啟、關閉、加入、單獨成組四種之一。另一方面,每個合法分組也能唯一對應到這些開閉操作。因此 DP 既不漏算,也不重算。
相鄰差值的貢獻要在決定目前工程師角色之前加入,因為這段距離是「前一個能力值到目前能力值」之間,被先前已開啟、尚未關閉的團隊跨過。
複雜度分析
- 時間複雜度:。共有 個位置,開啟團隊數最多 ,累積代價最多 。
- 空間複雜度:。使用滾動陣列後,只保留「開啟團隊數 累積代價」兩維狀態。
Code
1 | MOD = int(1e9 + 7) |
寫在最後
The cover image was created by @崎白. All rights belong to the original artist.
It is used here only as a non-commercial cover illustration for this note. I do not claim ownership of the artwork.
If you are the copyright holder and believe this usage is inappropriate, please contact me by email or leave a comment. I will remove the image promptly.





