מבני נתונים ואלגוריתמים - מחברת קורס/מבחן סופי מועד א' סמסטר קיץ 2007 - תשובות
מתוך ויקיספר, אוסף ספרי הלימוד והמדריכים החופשי.
תוכן עניינים |
[עריכה] שאלה 1
ראשית נבנה את רשת הזרימה המתוארת בתרשים הבא. בגרף זה
צמתים:
- צמתים
, אחד לכל סטודנט - צמתים
, אחד לכל חברה - צומת מקור
וצומת בור 
נגדיר כ
את Length(W).
בגרף יש
קשתות:
- קשת אחת לכל איבר ב
Wבעלת קיבול 1 - קשת מ
לכל
בעלת קיבול 1 - קשת מכל
ל
בעלת קיבול ![\displaystyle P[j]](http://upload.wikimedia.org/math/f/6/7/f67c85c2b1a780fe71772dcb871bf909.png)
כעת נריץ את אלגוריתם Ford-Fulkerson. נחזיר כתשובה כל זוג
שעבורו קיבלנו
.
|
משפט: האלגוריתם מחזיר תשובה נכונה. |
הוכחה: אנו יודעים שאלגוריתם Ford-Fulkerson משפר תמיד בגדלים שלמים (שהרי הקיבולים שלמים). קל מאד להוכיח (פורמלית, באינדוקציה), שבמקרה זה, חלק מהקשתות מ
ל
יזרימו 1, והשאר 0 (אלו האפשרויות היחידות). נצייר את הגרף המתקבל ממחיקת הקשתות שאינן מזרימות כלום:
נניח שיש קשת בין
ל
. בהכרח הקשת מזרימה 1. מצד שני, הקשת המזרימה מ
ל
מזרימה גם היא 1. משימור הזרם נובע אם כן שאין קשת אחרת היוצאת מ
.טענה כמעט זהה מראה שאין קשת אחרת הנכנסת ל
. ברור גם שקשת זו היתה שייכת לגרף המקורי. מכאן נובע שהמערך המוחזר W' אכן משייך אחת לאחת סטודנט לחברה, ואינו ממציא שיוכים שלא הופיעו במקור. קל גם לראות שכל חברה אינה מעסיקה יותר סטודנטים מיכולתה. קל לראות שאם יש התאמה גדולה יותר, אז אפשר להגיע לזרימה גדולה יותר מהמושגת כאן, וזאת בסתירה לנכונות Ford-Fulkerson.
|
משפט: סיבוכיות האלגוריתם |
הוכחה: אם משתמשים ברשימת שכנויות, אז בניית הגרף החדש היא
.כבר ראינו בניתוח אלגוריתם Ford-Fulkerson, שסיבוכיותו לינארית במספר הקשתות כפול גודל הזרימה המירבית. מספר הקשתות כאן הוא
, והזרימה המירבית היא 
[עריכה] שאלה 2
[עריכה] המבנה הרקורסיבי
נגדיר כ
את העלות הזולה ביותר לטיפול בקטע באורך
.
|
משפט:
|
הוכחה: נשים לב לנקודות הבאות:
- אם
, אז אין מה לחתוך; ההובלה תעלה
. - אם
, אז או שלא חותכים את הקטע, או שכן. אם לא חותכים, עלות השבירה 0, ועלות ההובלה
. אם חותכים בנקודה
, אז עלות הטיפול בחלק השמאלי תהיה , עפ"י ההגדרה,
, עלות השבירה היא כמובן
, ועלות הטיפול בחלק הימני תהיה, עפ"י ההגדרה,
. נותר לקחת מינימום על פני
, ולהחליט האם לחתוך או לא.
|
כדאי לדעת: אפשר כמובן לחשוב על מבנה רקורסיבי אחר לבעיה, כמו לדוגמה המבנה בדג הסלמון. |
[עריכה] מימוש נאיבי
להלן פסוודו-קוד המממש את נוסחת הנסיגה.
Min-Cost(i) 1 if i == 1 2 return F(1) 3 guess-without = F(i) 4 guess-with = ∞ 5 for j in [1, …, i - 1] 6 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 7 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 8 if guess-with < guess-without 9 return guess-with 10 else 11 return guess-without
[עריכה] שימוש בmemoization
נוסיף memoization.
Min-Cost(i) 1 if M[i] != Nil 2 return M[i] 1 if i == 1 2 M[1] = F(1) 3 return F(1) 4 guess-without = F(i) 5 guess-with = ∞ 6 for j in [1, … i - 1]] 7 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 8 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 9 if guess-with M[i] < guess-without 10 M[i] = guess-with 11 return guess-with 12 else 13 M[i] = guess-without 14 return guess-without
(נניח שהמערך הגלובלי Mבאורך nמאותחל תחילה כולו לNil.)
[עריכה] הדפסת נקודות השבירה
נוסיף גם מספיק מידע לצורך הדפסת פרטי הפתרון.
Min-Cost(i) 1 if M[i] != Nil 2 return M[i] 1 if i == 1 2 M[1] = F(1) 3 Break[1] = Nil 4 return F(1) 5 guess-without = F(i) 6 guess-with = ∞ 7 for j in [1, … i - 1]] 8 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 9 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 10 if guess-with 11 Break[i] = j 12 if guess-with 13 M[i] = guess-with 14 return guess-with 15 else 16 M[i] = guess-without 17 return guess-without
המערך הגלובלי Break מכיל את ההחלטה שעשינו לגבי כל קטע. Break[i]הוא Nilאם החלטנו לא לחלק את הקטע, והוא
אם החלטנו לשבור בנקודה j.
כעת נדפיס את העצירות, ע"י קריאה לPrint-Breaks(n, 0). הפונקציה מוגדרת להלן.
Print-Breaks(i, start) 1 if Break[i] == Nil 2 return 3 j = Break[i] 4 Print(j + start) 5 Print-Nums(j, start) 6 Print-Nums(i - j, start + j)
הפונקציה מקבלת שני ארגומנטים: אורך הקטע, i, ותחילת הקטע, start. נניח שהפונקציה נקראה עבור שני ערכים ספיציפיים כאלה. אם Break[i] == Nil, אז לא חתכנו את הקטע, ואין מה להדפיס. אם לא, וBreak[i] == j, אז יש להדפיס את הנקודה j(תוך זכירה שאנו נמצאים startמתחילת כל הגשר), את החלק השמאלי, ואת החלק הימני.
[עריכה] ניתוח סיבוכיות
נגדיר את זמן הריצה של Min-Time(i) בהנחה שכל קריאה רקורסיבית היא
כ
. קל לראות ש
. זמן הריצה חסום מלמעלה על ידי
.
גם כאשר לוקחים בחשבון את האתחול והדפסת הפרטים, הפתרון עדיין
.
[עריכה] שאלה 3
|
שימו לב: אלגוריתם Dijkstra אינו פועל בצורה נכונה על גרפים בעלי קשתות שליליות (אפילו אם אין מעגל שלילי). לא למדנו אחרת, וקל לבנות דוגמה המראה זאת. |
נניח שזהו הגרף המקורי:
נבנה גרף חדש כך.
ראשית, נקח את צמתי הגרף
, ונשכפל אותם 3 פעמים, בקבוצות
,
, ו
. לכל צומת
בקבוצה
, יש צומת
ב
, וצומת
ב
. התרשים הבא מראה זאת.
כעת נוסיף גם קשתות:
- במקום כל קשת
בגרף המקורי, למעט הקשת השלילית, נמתח קשת מהצומת המתאים ל
ב
לצומת המתאים ל
ב
. - במקום כל קשת
בגרף המקורי, למעט הקשת השלילית, נמתח קשת מהצומת המתאים ל
ב
לצומת המתאים ל
ב
. - במקום הקשת השלילית
(בגרף המקורי), נמתח קשת מהצומת המתאים ל
ב
לצומת המתאים ל
ב
במחיר 0. התרשים הבא מראה זאת.
נניח שלקשת השלילית היה מחיר
. בנוסף נמתח עוד שני סוגי קשתות:
- לכל צומת
ב
, נמתח קשת במחיר
בין
ב
לבין
ב
. - לכל צומת
ב
, נמתח קשת במחיר 0 בין
ב
לבין
ב
.(לא נצייר קשתות אלו מטעמי נוחות.)
נשים לב שאם כל הגרפים הנ"ל מיוצגים ברשימת שכנויות, אז סיבוכיות הבניה היא
.
המשפט הבא מסביר את חשיבות הגרף החדש.
|
משפט: מחיר המסלול הזול ביותר מ |
הוכחה: בגרף המקורי, ייתכן שהמסלול הזול ביותר מ
ל
לא השתמש בקשת השלילילת, וייתכן שהשתשמש בו. במקרה הראשון, תהיה קפיצה מ
ישירות ל
, ובמקרה השני, תהיה קפיצה מ
ל
ומ
ל
. הקפיצה מ
ישירות ל
עולה
יותר מאשר המסלול המקורי. הקפיצות מ
ל
ומ
ל
גם כן עולות
יותר מאשר המסלול המקורי, שכן עלות כל אחת משתי הקשתות היא 0, והנוסע לא קיבל את התשלום
מהחברה.
כעת כל הקשתות אינן שליליות, ומותר להריץ את Dijkstra.
קל לראות שהסיבוכיות הכוללת היא זו של Dijkstra.
[עריכה] שאלה 4
- הטענה נכונה. נניח שבערימה יש
איברים. האיבר האחרון בערימה הוא באינדקס
, ואביו (אם יש לו) בהכרח יושב ב
. לכן, למרות שהלולאה אינה עוברת על כל האיברים, היא מדלגת רק על ערימות בעלי איבר יחיד, שהן, עפ"י תכונת הערימה, תקינות. הלולאה תתקן כל ערימה שדורשת תיקון, בדיוק מהסיבה שראינו בהרצאה. הסיבוכיות היא
. אפשר לראות שהלולאה עוברת על חצי מהאיברים, ולכן היא
. מצד שני, היא עושה פחות מBuild-Heap, וזו היתה
, ולכן גם לולאה זו
. - הטענה נכונה. אם ילדו השמאלי של איבר במקום
נמצא באינדקס
, וילדו הימני נמצא באינדקס
, אז כל מסלול מהשורש לעלה עובר על סדרת אינדקסים מונוטונית עולה. לכן, אם המערך ממויין, כל מסלול מהשורש לעלה יעבור על סדרת ערכים מונוטונית לא-יורדת. הסיבוכיות הנה מיון מיזוג, כלומר
. - הטענה אינה נכונה, ומפורכת (לדוגמה) ע"י
. הסיבוכיות הנה
, בדיוק מהניתוח שכבר ראינו לגבי הפתרון מההרצאה. - הטענה נכונה. היות ש
Bubble-Up(bh, i)אינו יכול להשפיע על איברים באינדקס מעל
, הקוד שקול לסדרת פעולות Insert. מאותה סיבה, הסיבכויות היא זו של סדרת פעולות Insert, כלומר
. - הטענה אינה נכונה, ומפורכת (לדוגמה) ע"י
. הסיבכויות היא זו של סדרת פעולות Insert, כלומר
.


.
.

