מבני נתונים ואלגוריתמים - מחברת קורס/מבחן סופי מועד א' סמסטר ב' 2007 - תשובות
הבהרה בנוגע לשימוש בלשון זכר: כדי למנוע סרבול מיותר, וכמקובל בשפה העברית, ננקטת לעתים לשון זכר בהתייחסות אל כלל הגולשות והגולשים ובמתן שמות לערכים. אנא קבלו זאת בהבנה.
שאלה 1
עריכההפתרון דומה מאד להוכחת הטענה שעצים אדומים-שחורים הם לוגריתמיים. נניח שגובה העץ הוא . מה מספר המפתחות הקטן ביותר היכול להיות בעץ? קל לראות שזה המספר המתקבל כאשר כל אחד מהצמתים הוא מסוג Bar
. במצב זה, היות שעל פי הנתונים כל המסלולים משורש לעלה הם בעלי אותו אורך, נקבל שמספר המפתחות הוא לכל הפחות . הפתרון, לכן, מתקבל על ידי פתירת אי-השוויון :
שאלה 2
עריכהנניח שזהו הגרף המקורי:
נבנה גרף חדש כך.
ראשית, נקח את צמתי הגרף , ונשכפל אותם 3 פעמים, בקבוצות , , ו . לכל צומת בקבוצה , יש צומת ב , וצומת ב . התרשים הבא מראה זאת.
כעת נוסיף גם קשתות:
- כל קשת בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין ב לבין הצומת המתאים ל ב .
- כל קשת בין ל (בגרף המקורי) - נמתח אותה בגרף החדש בין הצומת המתאים ל ב לבין הצומת המתאים ל ב .התרשים הבא מראה זאת (רק חלק מהקשתות מצויירות).
בנוסף נמתח עוד שני סוגי קשתות:
- לכל צומת ב , נמתח קשת במחיר בין ב לבין ב .
- לכל צומת ב , נמתח קשת במחיר בין ב לבין ב .(לא נצייר קשתות אלו מטעמי נוחות.)
נשים לב שאם כל הגרפים הנ"ל מיוצגים ב[[מבני נתונים ואלגוריתמים - מחברת קורס/#|]], אז סיבוכיות הבניה היא .
המשפט הבא מסביר את חשיבות הגרף החדש.
משפט: מחיר המסלול הזול ביותר מ ל בגרף המקורי (כולל הקנס, אם יש), קטן בדיוק ב1 ממחיר המסלול הזול ביותר מ ל ב בגרף החדש. |
הוכחה: מבניית הגרף החדש, ברור שכל מסלול מ ל ב בגרף החדש, מורכב משני חלקים:
- מסלול זהה לחלוטין למסלול מ ל בגרף המקורי.
- מסלול בעלות 1 ל ב , אם החלק הראשון זוגי, ומסלול בעלות , אם החלק הראשון אי-זוגי.
לכן, כל שנותר הוא להריץ את אלגוריתם Dijkstra.
קל לראות שהסיבוכיות הכוללת היא זו של אלגוריתם Dijkstra.
שאלה 3
עריכהכבר ראינו את נוסחת הנסיגה . נשתמש בזאת כדי להוכיח את הטענה בשאלה באינדוקציה. נראה שקיים כך שתמיד .
הוכחה: (מעבר האינדוקציה) נניח שהטענה נכונה עד (כולל) , ונראה שהיא נכונה גם
ל .
נפתור , ונקבל . עבור , נקבל שמספיק שיתקיים .
מבדיקת מספר העצים השונים, עולה שקיים מתאים גם לבסיס האינדוקציה (המקרים ).
שאלה 4
עריכהראשית נניח שהמערך M
ממויין, או, לחלופין, נמיין אותו בעזרת מיון מיזוג בסיבוכיות .
נגדיר כ את מירב הרווח שאפשר להרוויח מפתיחת סניפים הלקוחים מתוך הבתים הראשונים.
משפט: לפי הגדרת :
|
הוכחה: (2)
אם , אז ברור שהפתרון הרווחי ביותר הוא זה שבו בוחרים את הבית הראשון (והיחידי האפשרי לבחירה). אם , אז או שבוחרים את בית , או שלא. במקרה הראשון, מרוויחים מהבחירה בבית, אך לפניו אסור לבחור באף בית בעל מיקום שהפרשו מ קטן מ . במקרה השני, הרווח הוא בדיוק הרווח כשמותר לבחור מבין הבתים הראשונים.
להלן פסוודו-קוד המממש נוסחה זו.
Max-Profit(M, P, k, i)
1 if i == 0
2 return 0
3 if i == 1
4 return P[1]
5 guess-without = Max-Profit(i - 1)
6 guess-with = P[i]
7 if m[i] > k
8 j = Find-Index(M, M[i] - k)
9 guess-with = guess-with + Max-Profit(M, P, k, j)
10 if guess-with > guess-without
11 return guess-with
12 else
13 return guess-without
חשוב רק לשים לב לקריאה לפונקציה Find-Index
ב8. פונקציה זו מוצאת את האינדקס הגדול ביותר בM
שקטן מM[i] - k
. היות שM
ממויין, נוכל לעשות זאת על ידי שינוי קטן בחיפוש בינרי. נוסיף גם Memoization:
1 MP = Make-Array(n)
2 for i in [1, ..., n]
3 MP[i] = Nil
Max-Profit(M, P, k, i)
1 if i == 0
2 return 0
3 if MP[i] != Nil
4 return MP[i]
5 if i == 1
6 MP[1] = P[1]
7 return P[1]
8 guess-without = Max-Profit(i - 1)
9 guess-with = P[i]
10 if m[i] > k
11 j = Find-Index(M, M[i] - k)
12 guess-with = guess-with + Max-Profit(M, P, k, j)
13 if guess-with > guess-without
14 MP[i] = guess-with
15 return guess-with
16 else
17 MP[i] = guess-without
18 return guess-without
כעת נוסיף גם את הדפסת האיברים:
1 MP = Make-Array(n)
2 Used = Make-Array(n)
3 for i in [1, ..., n]
4 MP[i] = Nil
Max-Profit(M, P, k, i)
1 if i == 0
2 return 0
3 if MP[i] != Nil
4 return MP[i]
5 if i == 1
6 MP[1] = P[1]
7 Used[1] = True
8 return P[1]
9 guess-without = Max-Profit(i - 1)
10 guess-with = P[i]
11 if m[i] > k
12 j = Find-Index(M, M[i] - k)
13 guess-with = guess-with + Max-Profit(M, P, k, j)
14 if guess-with > guess-without
15 MP[i] = guess-with
16 Used[i] = True
17 return guess-with
18 else
19 MP[i] = guess-without
20 Used[i] = False
21 return guess-without
המערך Used
מתאר האם השתמשנו באיבר ה או לא. נשתמש בו ע"י קריאה לPrint-Nums(k, n)
, כאשר Print-Nums
מוגדרת כך:
Print-Nums(k, i)
1 if i &lt 1
2 return
3 if Used[i]
4 j = Find-Index(M, M[i] - k)
5 Print-Nums(k, j)
6 else
7 Print-Nums(k, i - 1)
8 if Used[i]
9 Print(i)
נותר רק לנתח את הסיבוכיות.
השורה j ← Find-Index(M, M[i] - k)
אורכת . אם נגדיר כ את זמן הקריאה לMax-Profit(M, P, k, i)
בהנחה שכל קריאה רקורסיבית היא , אז , והסיבוכיות היא . מצד שני, מיון המערך הוא , ולכן נקבל .
שאלה 5
עריכהניצור שתי קבוצות. הקבוצה השמאלית, תכיל בתחילה את כל הצמתים (כלומר ); הקבוצה הימנית, , תהיה ריקה בתחילה (כלומר ).
נאמר שקשת כלשהי שמחה אם ו שייכות לשתי קבוצות שונות. נגדיר כ את קבוצת הקשתות היוצאות מ , ונגדיר כ את קבוצת הקשתות השמחות היוצאות מ . לפי הגדרות אלו:
- בתחילה, אף קשת אינה שמחה. ( .)
- אם לפחות חצי מהקשתות היוצאות מכל צומת שמחות, פתרנו את הבעיה. (אם , אז פתרנו את הבעיה.)
כעת, כל עוד לא פתרנו את הבעיה, נפעל כדלהלן. נבחר שרירותית צומת כלשהו מבין הצמתים שרוב קשתותיהם אינו שמח. נעביר את לקבוצה השניה.
דוגמה: בתרשים הבא, . לכן נעביר את ל . |
המשפט הבא מראה שהתהליך הוא סופי.
משפט: בכל פעם שמעבירים צומת כלשהו כמתואר לעיל, עולה מספר הקשתות השמחות. |
לפני הוכחת משפט זה, הבה נבין מדוע הוא מראה שהתהליך סופי. המשפט טוען שכל עוד קיים צומת כלשהו שרוב קשתותיו אינו שמח - העברת לקבוצה השניה תגדיל את מספר הקשתות השמחות. מספר הקשתות השמחות מתחיל ב0, ויכול להגיע לכל היותר ל .
כעת נוכיח את המשפט.
הוכחה: נניח שהשכנים של בקבוצה שלו הם , ושכניו בקבוצה השניה הם . נשים לב שבהכרח .
לאחר העברת , מספר הקשתות השמחות גדל ב .