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