第一章 資料結構基礎
1-1 演算法
[演算法(algorithm)] 21 | 91,93,94(4),97,99(2),101,102,104,106,107(3),109(2),110,111
91關薦、93檢三、94公員晉高、94身三、94檢三(2)、97關三、99地三(2)、101地三、102地三、103高三、104地三、106關三、107地三(3)、109高三、109地三、110地三、111地三
一、演算法特性:輸入+輸出+明確+有限+有效
(一)輸入(Input):可以輸入或不輸入 (亂數產生)。
(二)輸出(Output):需有結果產生。
(三)明確性(Definiteness)
每一行指令都必須明確,不可模稜兩可。例如成績優秀的學生才能領獎學金就不具有明確性,因為每個人對成績優秀的定義可能不相同,如果改為成績90分以上的學生才能領獎學金,則具有明確性,因為90分是比較客觀的定義。
註:y = x/0違反明確性。
※參考資料:李春雄-計算機概論第六章演算法.ppt
(四)有限性(Finiteness)
不能有無窮迴路,必須能終止執行,就是必須在有限步驟內完成。
註:無窮迴圈 (infinite loop) 違反有限性。
(五)有效性(Effectiveness)或正確性(Correctness):可以正確執行。
二、演算法工具
(一)工具:步驟條列、流程圖、虛擬碼 (pseudo code)、程式。
(二)舉例
以選擇排序法 (Selection Sort) 為例:
1.步驟條列:
step 1:輸入一組資料於輸入串列中,並且設定輸出串列為空串列。
step 2:由輸入串列中選取出最小 (或最大) 值,並且將它從輸入串列中刪除。
step 3:將取得的最小值 (或最大) 加入輸出串列的尾端 (或開頭)。
step 4:若輸入串列不是空的,則回到 step 2。
step 5:結束。
2.虛擬碼:
Function selectionSort(Type data[1..n]) Index i, j, max // 定義索引 For i←1 to n do min←i For j←i+1 to n do If data[j] < data[min] then min←j Exchange data[i] and data[min] End |
3.C語言的程式:
void Select_Sort(int data[ ], int n) { // data[ ] 是欲排序的資料,n 是陣列大小 int i, j, min, temp; for(i = 0; i < n-1; i++){ min = i; // 範圍內最左的鍵值索引 // 選擇出範圍內最小的鍵值,將它與範圍內最左的鍵值交換 for(j = i+1; j < n; j++) if(data[j] < data[min]) // 右鍵值小於左鍵值 min = j; // 最小鍵值索引為右鍵值索引 temp = data[min]; data[min] = data[i]; data[i] = temp; } } |
※參考資料:
1.http://program-lover.blogspot.tw/2008/08/selection-sort.html
2.http://freefeast.info/general-it-articles/selection-sort-pseudo-code-of-selection-sort-selection-sort-in-data-structure/
三、其他議題
(一)演算法與程式最大的不同
演算法具有「有限性」,程式不一定具有「有限性」。
(二)填魔術方陣的方法
以奇數最為簡單,第一個數字放在第一行第一列的正中央,然後向右 (左) 上填,如果右 (左) 上已有數字,則向下填,如下圖所示:
99年地特三等資料結構
五、考慮設計中式象棋 (如圖) 電腦程式系統:(每小題5分,共10分) (一)請設計一資料結構使能隨時表示出棋盤現狀 (current state),包含所有棋子的位置、有那些棋子在棋盤上。 (二)寫出一演算法能產生「象」或「相」在任意位置之下一步可前往且合規則的所有位置 (next feasible positions),注意,務必考慮其他棋子阻礙的因素。「象」或「相」的移動規則:(1)田字形的對角移動;(2)田字正中央有棋子時,不能移動前往。 |
答:
(一)
1.棋盤:
int board[10][9]; // 10*9 二維陣列棋盤,二維陣列的索引記錄目前棋盤上棋子的所在座標位置,二維陣列的值記錄目前棋盤是那顆棋子及是否沒棋子。
2.棋子代碼:
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
將 |
士 |
士 |
象 |
象 |
車 |
車 |
馬 |
馬 |
包 |
包 |
卒 |
卒 |
卒 |
卒 |
卒 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
帥 |
仕 |
仕 |
相 |
相 |
俥 |
俥 |
傌 |
傌 |
炮 |
炮 |
兵 |
兵 |
兵 |
兵 |
兵 |
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
空 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
例如 board[0][0] = 6 (車),座標位置 (0, 0);board[0][1] = 8 (馬),座標位置 (0, 1);board[2][1] = 4 (砲),座標位置 (2, 1)。
(二)
1.田字形的對角移動:
2.演算法:
void move(int i, int j, int direction) { // i, j 棋子座標,direction 移動方向 int temp = board[i][j]; // 舊棋子位置 if(board[i][j] == 4 || board[i][j] == 5 || board[i][j] == 20 || board[i][j] == 21) { // 如果是象或相 // 田字正中央有棋子時,不能移動前往 if((board[i-1][j-1] != NULL && i-1 != 0 && j-1 != 0) || (board[i-1][j+1] != NULL && i-1 != 0 && j+1 != 8) || (board[i+1][j-1] != NULL && i+1 != 9 && j-1 != 0) || (board[i+1][j+1] != NULL && i+1 != 9 && j+1 != 8)) { exit(1); // 不能移動前往 } if(board[i-2][j-2] != NULL && direction = 0) { // 不是邊界,並且往左上角移動 board[ i-2][j-1] = temp; // 新棋子位置 board[i][j] = 0; // 舊棋子位置清空 } else if(board[i-2][j+2] != NULL && direction = 1) { // 不是邊界,並且往右上角移動 board[ i-2][j-1] = temp; board[i][j] = 0; } else if(board[i+2][j+2] != NULL && direction = 2) { // 不是邊界,並且往右下角移動 board[i+2][j+1] = temp; board[i][j] = 0; } else if(board[i+2][j-2] != NULL && direction = 3) { // 不是邊界,並且往左下角移動 board[ i+2][j-1] = temp; board[i][j] = 0; } } } |
101年地方特考三等資料結構
四、迴文 (palindrome) 乃是一個字串不論從左到右或從右到左看結果一模一樣,例如 “油麻地遍地麻油”、“人人為我、我為人人”、“Fall leaves as soon as leaves fall” 等。 (二)請寫出演算法以測試輸入的字串 (字串的長度不超過100個字) 是否為迴文。(10分) |
答:
(二)
CreateStack( ); while(input not end) { x←read next character; // 從左自右逐一讀取字元 push(x); } scan string again; while(input not end) { x←read next character; // 從左自右逐一讀取字元 y = pop( ); // 取出堆疊的字元 if(x≠y) { print “not palindrome”; exit; } } print “is palindrome”; |
102年地方特考三等資料結構
一、請參考圖1: (三)假設圖1代表 heap 上各個節點 (node) 及其相互指向的關係。p、q、r 三節點代表全域變數 (global variables),其他的節點代表 heap 上的記憶體區塊。如果 p→a 的指標被消除,那些節點會變成無用的垃圾節點?你必須詳細描述尋找垃圾節點的方法及所需之資料結構。你的演算法只能從 a 節點出發,它必須指出所有的垃圾節點,並且你的演算法只能在每一個節點儲存很少量的資料。請問你的演算法必須在每一個節點儲存那些資料?(15分) |
答:
(三)
1.如果 p→a 的指標被消除,則 a, b, c, d, x 會變成圾垃節點,因為剩下2個全域變數指標 q→k 及 r→j 仍然可以拜訪其他節點。
2.尋找垃圾節點的演算法:
先將全部的節點都作記號;
For 每一個存在的節點 do 進行追蹤 // 從 q, r 二節點追蹤,p 無法追蹤
使用 DFS 或 BFS,將追蹤到的節點記號消除;
最後記號未消除的節點都是圾垃節點;
3.必須在每一個節點儲存布林值型態的旗標作記號,以便判斷是否為圾垃節點。
103年高考三級資料結構
五、若處理的資料,其數值均不同且已知均為1到100之間的整數或小數。若 K≦X<K+1,集合 Lx 代表數值在 [K, K-1] 間全部資料,1≦K≦99, K 為整數,資料結構支援下列功能。 1.Insert(X):增加 X,若 X 不存在 Lx 中。 2.Delete(X):移除 X,若 X 存在 Lx 中。 3.List(X):將 Lx 中的資料全部依序印出。 設計一資料結構滿足在最差情況的條件分析 (Worst Case Analysis),每個功能的執行時間要求為:Insert(X) and Delete(X) 須在 O(log|Lx|) 時間內完成,List(X) 則須在 O(|Lx|) 時間內完成。請說明設計的資料結構為何?並解釋其執行時間為何滿足需求?(15分) |
答:
處理的資料為1到100之間的整數或小數。先判斷 K 位於那個範圍 (1≦K≦99),然後建立99個首節點陣列存放 K 值,每個首節點陣列指向一個集合 Lx。此 Lx 為一棵 AVL-tree,存放帶有小數的正整數集合。
1.Insert(X):
由首節點陣列找到 Lx 的 AVL-tree root,再將資料 X 插入樹中,時間複雜度為 O(log|Lx|)。
2.Detete(X):
由首節點陣列找到 Lx 的 AVL-tree root,再將 X 由 AVL-tree 刪除,時間複雜度為 O(log|Lx|)。
3.List(X):
由首節點陣列找到 Lx 的 AVL-tree root,再對 AVL-tree 進行中序走訪,時間複雜度為 O(|Lx|)。
104年地方特考三等資料結構
三、(一)請說明使用何種資料結構及其演算法,可有效判斷一運算式 (expression)中的巢狀 (nested) 括號是否正確配對 (matched)。(10分) (二)請以兩個運算式實例 {A*[B-(C+D)+8]-16} 及 {A+[B-(C+5])},分別說明此演算法判斷的過程及結果。(10分) (注意:未說明判斷的過程,不予計分) |
答:
(一)
1.設計 A及 B 二個堆疊來存放運算子。左括號存放到堆疊 A,右括號存放到堆疊 B。
2.由左至右掃瞄運算式,遇到左括號放入堆疊 A。
3.由右至左掃瞄運算式,遇到右括號放入堆疊 B。
4.從堆疊 A 取出左括號,再從堆疊 B 取出右括號,如果括號能正確配對,則正確,反之錯誤。逐一配對直到堆疊 A、B 為空。
init. StackA, StackB is empty; while(scan expression left to right And not EOF) { x←read( ); if(x is 左括號) push StackA(x); } while(scan expression right to left And not EOF) { y←read( ); if(y is 右括號) push StackB(y); } } while(StackA And StackB not EOF) { x←pop(StackA); y←pop(StackB); if(x 與 y 是同一類型的左右括號) output “Match”; else { output “Not Match”; } } if(StackA and StackB empty) output “Match”; else output “Not Match”; |
(二)
1.{A*[B-(C+D)+8]-16}
把 {, [, ( 存放到堆疊 A。
( |
[ |
{ |
把 }, ], ) 存放到堆疊 B。
) |
] |
} |
取出堆疊 A 的左括號 (,與堆疊 B 的右括號 ),配對成功。
再取出堆疊 A 的左括號 [,與堆疊 B 的右括號 ],配對成功。
再取出堆疊 A 的左括號 {,與堆疊 B 的右括號 },配對成功。
堆疊 A、B 為空,配對成功。
2.{A+[B-(C+5])}
把 {, [, ( 存放到堆疊 A。
( |
[ |
{ |
把 }, ), ] 存放到堆疊 B。
] |
) |
} |
取出堆疊 A 的左括號 (,與堆疊 B 的右括號 ),配對失敗。不用繼續比對。
109年身心障礙人員三等資料結構
一、假設 A[1: n] 是一個矩陣,存有 n 個不同的整數,且已依序從小到大排列。給定一個整數 s,設計一個線性時間 (linear time) 的演算法,找出在 A[1: n] 中是否存在兩個相異之 A[i] 和 A[j],使得 A[i]+A[j] = s。若存在,則印出任一組符合條件之 i 和 j;若不存在,則印出0。(須詳述或證明所設計程式之正確性及其計算複雜度,否則不計分)(25分) |
答:
(一)演算法
#include <iostream> using namespace std; int A[ ] = {1, 3, 5, 7, 9}; struct node { int i; int j; struct node* next; }; typedef struct node* NODEPTR; // 印出所有符合 A[i]+A[j] = s 的資料 void printlist(NODEPTR head) { NODEPTR ptr = head; while(ptr != NULL) { cout << "A[" << ptr->i << "] = " << A[ptr->i] << ", "; cout << "A[" << ptr->j << "] = " << A[ptr->j] << endl; ptr = ptr->next; } cout << endl; } int main( ) { NODEPTR head, tail, prev; head = tail = prev = (NODEPTR)malloc(sizeof(node)); int s = 12; int n = 5; // 5筆資料 int i = 0; int j = n-1; bool check = false; // 檢查 A[i]+A[j] = s 是否存在 while(i < n-1) { if(i == j) { break; // 結束 } if(A[i]+A[j] > s) { // j 往左移 j--; } else if(A[i]+A[j] < s) {// i 往右移 i++; } else if(A[i]+A[j] == s) { // 找到 A[i]+A[j] = s check = true; tail->i = i; tail->j = j; tail->next = (NODEPTR)malloc(sizeof(node)); prev = tail; tail = tail->next; i++; continue; } } if(check == false) { // 如果 A[i]+A[j] = s 存在 printf("0\n"); } else { prev->next = NULL; cout << "A[" << head->i << "] = " << A[head->i] << ", "; cout << "A[" << head->j << "] = " << A[head->j] << endl; // printlist(head); } return 0; } |
執行結果:
A[1] = 3, A[4] = 9
(二)說明
索引 |
0 |
1 |
2 |
3 |
4 |
值 |
1 |
3 |
5 |
7 |
9 |
|
i |
→ |
|
← |
j |
1.當 i = n-1 或 i = j 時,則找不到 A[i]+A[j] = s。
2.找到 A[i]+A[j] = s 的最糟情況是 j-i = 1,此時搜尋次數為 n-2 次,時間複雜 度為 O(n)。
※參考資料:
https://yamol.tw/exam-109+%E5%B9%B4109+%E8%BA%AB%E5%BF%83%E7%89%B9%E8%80%83_%E4%B8%89%E7%AD%89_%E8%B3%87%E8%A8%8A%E8%99%95%E7%90%86%EF%BC%9A%E8%B3%87%E6%96%99%E7%B5%90%E6%A7%8B86485-86485.htm
109年高考三級資料結構
一、考慮數字1到 n,若將其順序重新排置,每個排列順序都稱作一個排列或置換 (Permutation),例如5 1 4 3 2是1 2 3 4 5的一個排列。我們可以將一個數字1到 n 的排列視為一個順序的映射 P,則前述例子可表示為 P(5) = 1、P(1) = 2、P(4) = 3、P(3) = 4、P(2) = 5。當然,1 2 3 4 5也是1 2 3 4 5的一個排列。在一個數字1到 n 的排列 P 中,若一對數字 i 和 j,1 ≦ i < j ≦ n,P( j) < P(i),也就是在排列 P 中較大的數字 j 出現在較小的數字 i 左邊(前面),我們稱此對數字為反向 (Inversion),而排列 P 的反向數 (Inversion number) 則定義為排列 P 中反向的總數量。請回答下列問題: (一)數字1到 n 的何種排列會有最大的反向數?最大反向數是多少?(5分) (二)若給定一個數字1到 n 的排列 P,請提出一個線性遞迴 (Linear Recursive) 的方式來算出排列 P 的反向數,並提供虛擬碼 (Pseudo-code) 與時間複雜度分析。(10分) |
答:
(一)
P(j) |
P(i) |
|
|
|
1 |
2 |
3 |
4 |
5 |
5 |
4 |
3 |
2 |
1 |
j |
i |
|
|
|
假設數字1到5,排列54321的反向有 (5, 4)、(5, 3)、(5, 2)、(5, 1)、(4, 3)、(4, 2)、(4, 1)、(3, 2)、(3, 1)、(2, 1)。也就是當 j = 5 時,比5小的 i 有4, 3, 2, 1;當 j = 4 時,比4小的 i 有3, 2, 1;當 j = 3 時,比3小的 i 有2, 1;當 j = 2 時,比2小的 i 有1;當 j = 1 時,比1小的 i沒有。由以上推論可知:
1.由大到小的排列會有最大的反向數。
2.最大反向數 = (n-1)+(n-2)+…+1+0 =。
※參考資料:
1.https://www.ndsu.edu/pubweb/~dyheuer/Inversions%20of%20Permutations.pdf
2.https://www.youtube.com/watch?v=YEIh1r_ho-A
(二)
1.程式碼:
#include <iostream> using namespace std; int Inversion(int *A, int n, int j, int i) { cout << "j = " << j << ", i = " << i << endl; if(j == n) { return 0; } else if(i == n) { return Inversion(A, n, j+1, j+2); } if(A[j] > A[i]) { return Inversion(A, n, j, i+1)+1; } else if(A[j] <= A[i]) { return Inversion(A, n, j, i+1); } } int main( ) { int A[ ] = {5, 4, 3, 12, 1}; int n = 5; int count = 0; cout << Inversion(A, n, 0, 1) << endl; return 0; } |
執行結果:
7
2.時間複雜度分析:
(n+(n-1)+(n-2)+…+1)+1 = n(n+2)/2+1,時間複雜度為 O(n2)。
1-2 演算法的分類
一、貪婪法(Greedy Method)
[Greedy演算法] 5 | 102,106,110(2),111
102專三、106關三、110關三、110高三、111關三
(一)定義:貪錢
1.設定一個特定的選擇程序,稱為貪婪準則,以便在每一個步驟中做出目前最有利的選擇,即局部最佳解。
註:
每一步面臨選擇時,都做眼前最有利的選擇,不考慮對將來是否有不良的影響。
2.主要是採用按部就班地方式來產生最佳解。
3.在每個階段都產生一個看似最佳的選擇 (使用貪婪準則得到局部最佳解)。一旦做出選擇,就不可再更改,並希望這樣的選擇可以得到全域的最佳解。
4.在一般情況下,其結果大多是非常接近最佳解。
※參考資料:https://www.cyut.edu.tw/~ckhung/b/al/greedy.php
(二)步驟
從某一起點開始,不斷的改進該解答 (尋找周圍的更佳解,然後移到該更佳解上),直到無法改進為止。
(三)特性
並不保證永遠可以得到最佳解,但是它仍是一個有效率的演算法,而且在很多時候也可以得到最佳解,或者得到跟最佳解相去不遠的解。
(四)實例
1.霍夫曼演算法:
(1)貪婪準則(Greedy Choice Property):
確實存在「以最少見兩碼為兄弟」的最佳編碼。注意此處的邏輯,同一個問題可能可以有很多組不同的最佳編碼。只需要證明 Huffman Encoding 編出來的是其中之一即可。
(2)最佳化子結構(Optimal Substructure Property):局部最佳解
從「小心挑選的小一號問題的最佳編碼」適當修改來的編碼,必為原問題的最佳編碼。
2.找零錢:
你去商店買東西,你掏出了一張一百元,買了一包 23 元的零食。售貨員須找零錢給你,但是你希望售貨員找給你的硬幣數目越少越好,如此口袋會輕一點。那麼售貨員會給你幾個硬幣呢?
(1)選擇程序(selection procedure):
售貨員開始找尋收銀機中最大幣值的硬幣,而且此時在他腦中用來選擇的準則是究竟哪一枚硬幣的幣值是目前最佳的選擇 (局部最佳解)。
(2)可行性檢查(feasibility check):
售貨員必須判斷他剛剛選擇的那一枚硬幣的幣值加上「目前顧客方已經收到的幣值總數」是否超過「應找給顧客的最後總數」。
(3)解答檢查(solution check):
a.售貨員必須檢查目前「已找給顧客方的零錢總數」是否等於「應找給顧客的最後總數」。
b.如果兩者不相等,則售貨員必須繼續利用他的選擇硬幣機制拿出硬幣,並且重複上述的過程直到「已找給顧客方的零錢總數」等於「應找給顧客的最後總數」或是收銀機裡的硬幣全部用盡為止。
註:答案的原則是先給你面額較高的硬幣。
3.最小生成樹的演算法 (Prim MST、Kruskal MST、Dijkstra’s algorithm)。
※參考資料:
1.https://www.cyut.edu.tw/~ckhung/b/al/greedy.php
2.http://acm.nudt.edu.cn/~twcourse/Greedy.html
3.https://zh.wikipedia.org/wiki/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95#應用
(五)適用時機
1.若遇最佳化問題,先思考可否用 Greedy Approach 解,若不行再考慮用 Dynamic Programming。
2.如果所要處理的最佳化問題無法找到一個選擇程序,則需要考慮所有的可能情況,就是屬於 Dynamic Programming。
※參考資料:
1.Sartaj Sahni-資料結構、演算法與應用使用C++ p.618~p.619
2.郭育倫-演算法導論-第12章貪婪演算法.ppt
3.(PTT)陳士杰-Course07_1 貪婪法則.ppt
4.http://ccckmit.wikidot.com/so:greedyalgorithm
106年關務三等資料結構
三、一個工廠有 n 台機器 M1, M2, …, Mn 及 k 份工作 J1, J2, …, Jk,每份工作都有其所需的執行時間 T(J1), T(J2), …,T(Jk)。每一台機器一次只能執行一份工作,每份工作只能交給一台機器執行,n 台機器可同時執行n 份不同的工作。 (一)請設計一個 Greedy (貪婪) 的演算法,來解決工作排程的問題,使得完成 k 份工作的時間最短。(15分) (二)此 Greedy 演算法適合使用何種資料結構來完成?(5分) (三)此 Greedy 演算法的解法是否能保證為最佳解?請舉例說明。(5分) |
答:
略
110年高考三級資料結構
二、假設收銀機內銅板的集合 S = {$50, $20, $20, $15, $10, $2, $1, $1, $1},而預計找錢給顧客的金額 W = $75。(一)請設計一個 Greedy (貪婪) 的演算法,來解決找錢給顧客的問題,使得找給顧客金額 W 所使用的銅板數量最少,並依此 Greedy 的演算法列出找給顧客金額 W = $75 的過程。(15分)(二)此 Greedy 演算法適合使用何種資料結構來完成。(5分)(三)此 Greedy 演算法的解法是否能保證為最佳解?請舉例說明。(5分) |
答:
(一)
#include <stdio.h> int main( ) { int coin[ ] = {50, 20, 20, 15, 10, 2, 1, 1, 1}; int sum = 75; int amount = 0; for(int i = 0; i < 9; i++) { int cnt = 0; if(sum >= coin[i]) { sum = sum-coin[i]; amount += 1; cnt += 1; printf("找回%d元銅板%d個\n", coin[i], cnt); } else { i++; } } printf("總共使用%d個銅板\n", amount); return 0; } |
執行結果:
找回50元銅板1個
找回20元銅板1個
找回1元銅板1個
找回1元銅板1個
找回1元銅板1個
總共使用5個銅板
過程:
1.第1次選擇50元銅板,剩下25元未找。
2.第2次選擇20元銅板,剩下5元未找。
3.第3次選擇2元銅板,剩下3元未找。
4.第4次選擇1元銅板,剩下2元未找。
5.第5次選擇1元銅板,剩下1元未找。
6.第6次選擇1元銅板,結束。
總共需要6個銅板。
(二)此 Greedy 演算法適合使用何種資料結構來完成。(5分)
可以使用陣列結構,將銅板依照價格由大到小儲存於陣列。
(三)此 Greedy 演算法的解法是否能保證為最佳解?請舉例說明。(5分)
不能保證為最佳解,最佳解應該是選擇50元銅板,15元銅板,10元銅板,總共需要3個銅板。
※0_1整數背包問題 (knapsack problem):貨物裝載問題(cargo loading problem)
110關三
(一)定義
「0/1」的意思是每種物品只會放進背包零個或一個。一個物品要就整個不放進背包、要就整個放進背包。物品無法切割。
※參考資料:https://web.ntnu.edu.tw/~algo/KnapsackProblem.html
110年關務三等資料結構
五、(一)如下圖設背包限重100,有 A、B、C、D、E 共五個不可分割物件,請 問依貪婪策略 (Greedy Algorithm),0_1整數背包問題 (knapsack problem) / 貨物裝載問題 (cargo loading problem) 其最大利益為何?其對應的0_1整數規劃為何?(20分) (二)有 A、B、C、D、E 共五個可分割物件,請問依貪婪策略,0_1 分數背包其最大利益為何?(10分) |
答:
假設考慮每單位重量的利益。依照「利益/重量」大小來排序,表格如下:
物件 |
重量 |
利益 |
利益/重量 |
C |
30 |
66 |
2.2 |
A |
10 |
20 |
2 |
B |
20 |
30 |
1.5 |
E |
50 |
60 |
1.2 |
D |
40 |
40 |
1 |
(一)
優先選擇「利益/重量」大的。
1.選擇 C,重量為30,利益66,重量剩下70。
2.選擇 A,重量為10,利益20,重量剩下60。
3.選擇 B,重量為20,利益30,重量剩下40。
4.選擇 E,重量為50,利益60,因為不可分割物件且重量過重,放棄選擇。
5.選擇 D,重量為40,利益40,重量剩下0。
依序選擇 C, A, B, D 物件,最大利益 = 66+20+30+40 = 156。
(二)
優先選擇「利益/重量」大的。
1.選擇 C,重量為30,利益66,重量剩下70。
2.選擇 A,重量為10,利益20,重量剩下60。
3.選擇 B,重量為20,利益30,重量剩下40。
4.選擇 E,因為可分割物件,所以重量為40,利益 = 40×1.2 = 48,重量剩下0。
依序選擇 C, A, B, E 物件,最大利益 = 66+20+30+48 = 164。
※參考資料:
1.https://www.tutorialspoint.com/design_and_analysis_of_algorithms/design_and_analysis_of_algorithms_01_knapsack.htm
2.https://www.tutorialspoint.com/design_and_analysis_of_algorithms/design_and_analysis_of_algorithms_fractional_knapsack.htm
111年關務三等資料結構
三、7個工作的利潤及處理的最後期限如下表所示,並假設每個工作的處理時間均為一個時間單位,請求出最適排程及最大利潤 (maximal profit) 為何?(20分)
|
答:
原始表格:
工作編號 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
利潤 |
40 |
15 |
60 |
20 |
45 |
10 |
55 |
最後期限 |
2 |
4 |
3 |
2 |
1 |
3 |
1 |
(一)選擇目前利潤最大者
依照利潤由大而小排序後的表格:
工作編號 |
3 |
7 |
5 |
1 |
4 |
2 |
6 |
利潤 |
60 |
55 |
45 |
40 |
20 |
15 |
10 |
最後期限 |
3 |
1 |
1 |
2 |
2 |
4 |
3 |
1.第1天:
工作編號 |
3 |
7 |
5 |
1 |
4 |
2 |
6 |
利潤 |
60 |
55 |
45 |
40 |
20 |
15 |
10 |
最後期限 |
1 |
1 |
1 |
2 |
2 |
4 |
3 |
從工作編號1~7中,選擇工作編號3,工作編號7和5以後就無法選擇,因為期限只有1天。
2.第2天:
工作編號 |
1 |
4 |
2 |
6 |
利潤 |
40 |
20 |
15 |
10 |
最後期限 |
2 |
2 |
4 |
3 |
從工作編號1、4、2和6中,選擇工作編號1,工作編號4以後就無法選擇,因為期限只有2天。
3.第3天:
工作編號 |
2 |
6 |
利潤 |
15 |
10 |
最後期限 |
4 |
3 |
從工作編號2和6中,選擇工作編號2,工作編號6以後就無法選擇,因為期限只有3天。
4.第4天:
工作編號 |
|
利潤 |
|
最後期限 |
|
沒有選擇工作編號可以選擇。
5.結論:
最適排程為工作編號依序為3→1→2;最大利潤為60+40+15 = 115。
(二)選擇最後期限中利潤最大者
依照最後期限由小而大排序後的表格:
工作編號 |
7 |
5 |
1 |
4 |
3 |
6 |
2 |
利潤 |
55 |
45 |
40 |
20 |
60 |
10 |
15 |
最後期限 |
1 |
1 |
2 |
2 |
3 |
3 |
4 |
1.第1天:
工作編號 |
7 |
5 |
1 |
4 |
3 |
6 |
2 |
利潤 |
55 |
45 |
40 |
20 |
60 |
10 |
15 |
最後期限 |
1 |
1 |
2 |
2 |
3 |
3 |
4 |
從工作編號7和5中,選擇工作編號7,工作編號5以後就無法選擇,因為期限只有1天。
2.第2天:
工作編號 |
1 |
4 |
3 |
6 |
2 |
利潤 |
40 |
20 |
60 |
10 |
15 |
最後期限 |
2 |
2 |
3 |
3 |
4 |
從工作編號1和4中,選擇工作編號1,工作編號4以後就無法選擇,因為期限只有2天。
3.第3天:
工作編號 |
3 |
6 |
2 |
利潤 |
60 |
10 |
15 |
最後期限 |
3 |
3 |
4 |
從工作編號3和6中,選擇工作編號3,工作編號6以後就無法選擇,因為期限只有3天。
4.第4天:
工作編號 |
2 |
利潤 |
15 |
最後期限 |
4 |
選擇工作編號2。
5.結論:
最適排程為工作編號依序為7→1→3→2;最大利潤為55+40+60+15 = 170。
留言列表