# APCS實作題2018年:置物櫃分配 > 日期:2023年9月9日 > 作者:王一哲 > 題目來源:107年6月實作題 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=e465) <br /> ## 題目 ### 問題描述 你是個櫃子租借商,總共擁有 $M$ 個櫃子。現在這 $M$ 個櫃子分別被 $N$ 個人借用,借用量分別為 $(x_0, x_1, x_2, \dots, x_{N-1})$ 個櫃子,其中 $x_0 + x_1 + x_2 + \dots + x_{N-1} \leq M$。 現在你想要使用 $S$ 個空櫃子,在被借用的櫃子只能夠**全退**或**全不退**之下,最少需要請這 N 個人退還多少櫃子?也就是當有一個人借用10個櫃子時,不能夠只請他退還5個櫃子。 舉例來說,對於 $M = 20$ 個櫃子,現在分別被5個人借用 (1, 7, 2, 8, 2) 個櫃子,在需要使用 $S = 14$ 個櫃子之下,最少需要請他們退還 $7 + 8 = 15$ 個櫃子。 <br /> ### 輸入格式 第一行有三個正整數 $M$、$S$、$N$,分別代表櫃子總數 $M$、想要使用的櫃子數 $S$、借用人數 $N$。 $$ M \leq 100000, ~~~~~ S \leq M, ~~~~~ N \leq 100000 $$ 第二行有 $N$ 個非負整數 $x_0, x_1, x_2, \dots, x_{N-1}$,代表每個人所借用的櫃子數量。 其中 $x_0 + x_1 + x_2 + \dots + x_{N-1} \leq M$。 <br /> ### 輸出格式 最少需要請這 $N$ 個人退還的櫃子總數 <br /> ### 範例:輸入 ``` 20 14 5 1 7 2 8 2 ``` ### 範例:正確輸出 ``` 15 ``` <br /> ## Python 程式碼 以下的程式碼主要參考[這個網頁](https://david-chien.github.io/zj/zj_e465_1.html),使用 bitset 求指定資料可能的總合主要參考 LeetCode 416. Partition Equal Subset Sum 討論區 [C++ Bitset Solution](https://leetcode.com/problems/partition-equal-subset-sum/solutions/950597/c-bitset-solution/)。其中最難理解的部分是第3、4行,以 ZeroJudge 網頁的範例來說明,由於已借出的格子數為 $1, 7, 2, 8, 2$,可能的總合有 $1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20$,如果 possSum 先設定為1,先將 possSum 向左移1位再與原本的值取 or 運算,其2進位值為 ```python 0b11 ``` 這代表 possSum 可能為 1。接下來再向左平移7位並與原本的值取 or 運算,其2進位值為 ```python 0b110000011 ``` 這代表 possSum 可能為 1、7、8,也就是看2進位值中哪幾個位數是1,就代表總合可能是這幾個位數。接下再代入 2、8、2 的結果依序為 ```python 0b11110001111 0b1111000111110001111 0b111111011111110111111 ``` 再由最後一位向前搜尋字串1,如果 target 對應的位數不是1,再繼續向前找,找到的位數就是答案。 <br /> 於 ZeroJudge 測試結果,最長運算時間約為 46 ms,使用記憶體最多約為 3.5 MB,通過測試。 ```python= def solve(array, tar): possSum = 1 for x in array: possSum |= (possSum << x) s = bin(possSum)[2:] for i in range(len(s)): if i >= tar and s[len(s)-i-1] == '1': return i M, S, N = map(int, input().split()) data = list(map(int, input().split())) left = M - sum(data) # 目前剩下的空格數 target = S - left # 需要歸還的格子數量 if target <= 0: # 如果 target <= 0 不需要歸還格子,印出0 print(0) else: print(solve(data, target)) ``` <br /><br /> ## C++ 程式碼 於 ZeroJudge 測試結果,最長運算時間約為 7 ms,使用記憶體最多約為 704 kB,通過測試。 ```cpp= #include <iostream> #include <vector> #include <bitset> using namespace std; int solve(vector<int> array, int tar); int main() { int M, S, N; cin >> M >> S >> N; vector<int> data (N, 0); int total = 0; for(int i=0; i<N; i++) { cin >> data[i]; total += data[i]; } int left = M - total; // 目前剩下的空格數 int target = S - left; // 需要歸還的格子數量 if (target <= 0) // 如果 target <= 0 不需要歸還格子,印出0 cout << 0 << endl; else cout << solve(data, target) << endl; return 0; } int solve(vector<int> array, int tar) { bitset<1000000> possSum; possSum[0] = 1; for(auto x : array) possSum |= (possSum << x); for(int i=tar; i<1000000; i++) { if (possSum[i] == 1) return i; } return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`