# APCS實作題2021年11月第3題:生産線 > 日期:2024年7月31日 > 作者:王一哲 > [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=g597) ## 題目 ### 問題描述 有 $n$ 台機器排成一直線,每一個機器都有一個數值 $t[i]$,代表該台機器要產出一單位的資料需要 $t[i]$ 單位的時間。 接下來有 $m$ 個工作要完成,每一個工作都需要位置在 $l[i], r[i]$ 的機器各生產出 $w[i]$ 單位資料。 現在你可以調換 $n$ 台機器的順序,目標是使得這 $i$ 個工作做完的總時間要最小。 <br /> ### 輸入說明 先輸入兩個正整數 $n$ 和 $m$ 代表有 $n$ 台機器和 $m$ 個工作。 接下來有 $m$ 行,每行有三個正整數 $l[i], r[i], w[i]$,代表第 $i$ 個工作需要編號從 $l[i]$ 到 $r[i]$ 的機器完成,並且需要各產生出 $w[i]$ 單位的資料。 最後一行包含 $n$ 個正整數 $t[1], t[2], \dots, t[n]$。 數字範圍 - $1 \leq n, m \leq 200000$ - $1 \leq w[i] \leq 100$ - $1 \leq t[i] \leq 100$ - $1 \leq l[i] \leq r[i] \leq n$ 子題配分 - 30%:$1 \leq n, m \leq 100, ~ w[i] = 1$ - 30%:$w[i] = 1$ - 40%:無額外限制 <br /> ### 輸出格式 輸出最小的總花費時間 <br /> ### 範例輸入1 ``` 5 1 2 4 1 1 2 3 4 5 ``` ### 範例輸出1 ``` 6 ``` ### 範例輸入2 ``` 10 3 2 5 6 3 6 4 7 8 1 1 2 3 4 5 6 7 8 9 10 ``` ### 範例輸出2 ``` 117 ``` <br /><br /> ## 解題的基本想法 如果想要得到最短的工作時間 time,需要使用産生1單位資料需時最短的機器處理最多的工作量。因此要先計算每部機器的工作量 loads,再將 loads 由小到大排序;再將機器依照産生1單位資料需要的時間 $w[i]$ 由大到小排序;將每部機器的工作量乘以需要的時間計算每部機器的工作時間 $t[i] = loads[i]*w[i]$,再加總 $t[i]$ 就是答案 time。 <br /><br /> ## Python 程式碼 ### 寫法1,讀取資料時用迴圈計算每部機器的工作量,會超時 由於第6、7行多了一組迴圈,到第6筆測資時會超時。 ```python= n, m = map(int, input().split()) # 機器數量n、工作數量 m loads = [0]*(n+1) # 每部機器的工作量,由於機器編號是 1 ~ n,串列長度要開 n+1 for _ in range(m): # 讀取 m 行資料 l, r, w = map(int, input().split()) # 左側機器編號 l,右側機器編號 r,工作量 w for i in range(l, r+1): # 更新第l到r機器的工作量,這兩行導致程式超時 loads[i] += w machines = list(map(int, input().split())) # 每部機器産生1單位資料需要的時間 machines.sort(reverse=True) # 機器由大到小排序 loads = loads[1:] # 刪除編號為0的機器 loads.sort() # 每部機器工作量由小到大排序 time = 0 # 答案,總工作時間 for load, machine in zip(loads, machines): # 計算答案 time += load*machine print(time) # 印出答案 ``` <br /><br /> ### 寫法2,利用差分數組計算每部機器的工作量 網路上找到的差分數組教學〈[小而美的算法技巧:差分数组](https://labuladong.online/algo/data-structure/diff-array/)〉。基本上就是記錄第$i$筆資料與第$i-1$筆資料的差值,如果 $[l, r]$ 區間的數值要加上 $w$,則差分數組 $diff[l] += w$,$diff[r+1] -= w$。 第18筆測資費時最久約為 1.1 s,使用記憶體最多約為 52 MB,通過測試。 ```python= n, m = map(int, input().split()) # 機器數量n、工作數量 m diff = [0]*(n+2) # 差分數組,長度要是對應的陣列長度加1 loads = [0]*(n+2) # 每部機器的工作量,機器編號是 1 ~ n,配合 diff 要開 n+2 for _ in range(m): # 讀取 m 行資料 l, r, w = map(int, input().split()) # 左側機器編號 l,右側機器編號 r,工作量 w diff[l] += w # 更新差分數組,[l, r] 區間工作量加 w diff[r+1] -= w cur = 0 # 目前讀取的 loads 數值 for i in range(1, n+2): # 依序産生 1 ~ n+1 loads[i] = cur + diff[i] # 更新 loads[i] cur = loads[i] # 更新 cur machines = list(map(int, input().split())) # 每部機器産生1單位資料需要的時間 machines.sort(reverse=True) # 機器由大到小排序 loads = loads[1:-1] # 刪除編號為 0 及 n+1 的機器 loads.sort() # 每部機器工作量由小到大排序 time = 0 # 答案,總工作時間 for load, machine in zip(loads, machines): # 計算答案 time += load*machine print(time) # 印出答案 ``` <br /><br /> ## C++ 程式碼 ### 寫法1,讀取資料時用迴圈計算每部機器的工作量,會超時 答案會超過 int 的上限,要用 long。由於第13行多了一組迴圈,到第10筆測資時會超時。 ```cpp= #include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速 int n, m; cin >> n >> m; // 機器數量n、工作數量 m vector<long> loads (n+1, 0); // 每部機器的工作量,由於機器編號是 1 ~ n,串列長度要開 n+1 for(int i=0; i<m; i++) { // 讀取 m 行資料 int l, r, w; cin >> l >> r >> w; // 左側機器編號 l,右側機器編號 r,工作量 w for(int j=l; j<=r; j++) loads[j] += (long)w; // 更新第l到r機器的工作量,此行導致程式超時 } vector<int> machines (n, 0); // 每部機器産生1單位資料需要的時間 for(int i=0; i<n; i++) cin >> machines[i]; sort(machines.begin(), machines.end(), greater<>()); // 機器由大到小排序 loads.erase(loads.begin()); // 刪除編號為0的機器 sort(loads.begin(), loads.end()); // 每部機器工作量由小到大排序 long time = 0; // 答案,總工作時間 for(int i=0; i<n; i++) time += loads[i]*(long)machines[i]; // 計算答案 cout << time << "\n"; // 印出答案 return 0; } ``` <br /><br /> ### 寫法2,利用差分數組計算每部機器的工作量 費時最久約為 0.1 s,使用記憶體最多約為 4.2 MB,通過測試。 ```cpp= #include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); // 加速 int n, m; cin >> n >> m; // 機器數量n、工作數量 m vector<long> diff (n+2, 0); // 差分數組 vector<long> loads (n+2, 0); // 每部機器的工作量 for(int i=0; i<m; i++) { // 讀取 m 行資料 int l, r; cin >> l >> r; // 左側機器編號 l,右側機器編號 r long w; cin >> w; // 工作量 w diff[l] += w; diff[r+1] -= w; // 更新差分數組 } long cur = 0; // 目前讀取到的 loads[i] 數值 for(int i=1; i<=n+1; i++) { // 用差分數組計算每部機器的工作量 loads[i] = cur + diff[i]; cur = loads[i]; } vector<int> machines (n, 0); // 每部機器産生1單位資料需要的時間 for(int i=0; i<n; i++) cin >> machines[i]; sort(machines.begin(), machines.end(), greater<>()); // 機器由大到小排序 loads.erase(loads.begin()); // 刪除編號為0的機器 loads.pop_back(); // 刪除最後一部多出來的機器 sort(loads.begin(), loads.end()); // 每部機器工作量由小到大排序 long time = 0; // 答案,總工作時間 for(int i=0; i<n; i++) time += loads[i]*(long)machines[i]; // 計算答案 cout << time << "\n"; // 印出答案 return 0; } ``` <br /><br /> --- ###### tags:`APCS`、`Python`、`C++`