# APCS實作題2023年1月第4題:機器出租
> 日期:2024年8月14日
> 作者:王一哲
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=j608)
## 題目
### 問題描述
有 $N$ 個活動,舉辦每個活動都要租借一台機器,若要舉辦第 $i$ 個活動,就需要在時間段 $[L_i, R_i]$ 租借用一台機器的。
已知 $N$ 個活動的需要使用的時間段,並且有 $K$ 台機器可以開放租借,且一台機器同時間只能借給一個活動,請問最多可以成功舉辦多少場活動?
註:時間段 $[2, 5]$ 與時間段 $[5, 10]$ 的兩個活動無法使用同一台機器,因為兩台機器都需要用到時段 $5$。
<br />
### 輸入說明
第一行有兩個正整數 $N$ 和 $K$。
第二行有 $N$ 個正整數,第 $i$ 個正整數是活動 $i$ 的開始時間 $L_i$。
第三行有 $N$ 個正整數,第 $i$ 個正整數是活動 $i$ 的結束時間 $R_i$。
子題配分:
- 20%:$1 \leq N \leq 100,~ K = 1$
- 20%:$1 \leq N \leq 100000,~ K = 1$
- 60%:$1 \leq N \leq 100000,~ K = 100$
所有測試資料都滿足 $0 \leq L_i, R_i \leq 10^8$
<br />
### 輸出格式
輸出一個整數表示最多可以舉辦多少活動
<br />
### 範例輸入1
```
5 1
0 2 1 3 4
2 3 5 4 6
```
### 範例輸出1
```
2
```
### 範例輸入2
```
8 2
3 1 4 3 7 2 2 5
5 3 7 4 8 7 4 6
```
### 範例輸出2
```
5
```
### 範例輸入3
```
2 1
1 2
2 3
```
### 範例輸出2
```
1
```
<br /><br />
## Python 程式碼
基本原則:
1. 活動越早結束,可以使用的機器越多。
2. 如果結束時間相同,優先選擇比較晚開始的活動,這樣使用機器的時間比較短,
為了達到這個要求,需要用第4 ~ 9行的自訂比較式,配合第15行的 cmp_to_key 將資料排序。
費時最久約為 1 s,使用記憶體最多約為 38.1 MB,通過測試。
```python=
from bisect import bisect_left
from functools import cmp_to_key
def mycmp(a, b): # 排序用的比較式,輸入 (開始時間, 結束時間)
if a[1] == b[1]: # 如果結束時間相同,依照開始時間由大到小排序
if a[0] < b[0]: return 1 # 符合條件的項往後放
else: return -1
if a[1] > b[1]: return 1 # 否則依照結束時間由小到大排序
else: return -1
N, K = map(int, input().split()) # 讀取 N、K
ls = list(map(int, input().split())) # 讀取開始時間
le = list(map(int, input().split())) # 讀取結束時間
se = list(zip(ls, le)) # 兩者組成串列
se.sort(key = cmp_to_key(mycmp)) # 排序
#print(se)
cnt = [-1]*K # 計錄使用中機器的結束時間,預設為 -1
ans = 0 # 答案,預設為 0
for s, e in se: # 由 se 依序讀取開始時間 s、結束時間 e
idx = bisect_left(cnt, s) # 找出 cnt 之中插入 s 的索引值
if idx == 0: continue # 如果 idx == 0,沒有可以使用的機器,找下一項
del cnt[idx-1] # 用 del 刪除前一項
cnt.append(e) # 將結束時間加入 cnt
ans += 1 # 數量加 1
#print(cnt)
print(ans) # 印出答案
```
<br /><br />
## C++ 程式碼
費時最久約為 55 ms,使用記憶體最多約為 1.1 MB,通過測試。
```cpp=
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
using namespace std;
bool mycmp(pair<int, int> a, pair<int, int> b) { // 排序用的比較式,輸入 (開始時間, 結束時間)
if (a.second == b.second) return a.first > b.first; // 如果結束時間相同,依照開始時間由大到小排序
return a.second < b.second; // 否則依照結束時間由小到大排序
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); // 加速
int N, K; cin >> N >> K; // 讀取 N、K
vector<pair<int, int>> se (N, make_pair(0, 0)); // 長度為 N 的二維陣列,儲存開始、結束時間
for(int i=0; i<N; i++) cin >> se[i].first; // 讀取開始時間
for(int i=0; i<N; i++) cin >> se[i].second; // 讀取結束時間
sort(se.begin(), se.end(), mycmp); // 排序
vector<int> cnt (K, -1); // 計錄使用中機器的結束時間,預設為 -1
int ans = 0; // 答案,預設為 0
for(auto v : se) { // 由 se 依序讀取資料
int s = v.first, e = v.second; // 開始時間 s、結束時間 e
int idx = lower_bound(cnt.begin(), cnt.end(), s) - cnt.begin(); // 找出 cnt 之中插入 s 的索引值
if (idx == 0) continue; // 如果 idx == 0,沒有可以使用的機器,找下一項
cnt.erase(cnt.begin() + idx - 1); // 用 erase 刪除前一項
cnt.push_back(e); // 將結束時間加入 cnt
ans++; // 數量加 1
}
cout << ans << "\n"; // 印出答案
return 0;
}
```
<br /><br />
---
###### tags:`APCS`、`Python`、`C++`