מבני נתונים ואלגוריתמים - מחברת קורס/מבחן סופי מועד א' סמסטר ב' 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, ויכול להגיע לכל היותר ל .
כעת נוכיח את המשפט.
הוכחה: נניח שהשכנים של בקבוצה שלו הם , ושכניו בקבוצה השניה הם . נשים לב שבהכרח .
לאחר העברת , מספר הקשתות השמחות גדל ב .