2016-03-29 116 views
0

所以,我試圖從我們的教科書中實現這個算法。使用遞歸算法解決揹包

enter image description here

我寫了這個:

// Knapsack_memoryfunc.cpp : Defines the entry point for the console application. 
//Solving Knapsack problem using dynamic programmig and Memory function 

#include "stdafx.h" 
#include "iostream" 
#include "iomanip" 
using namespace std; 

int table[20][20] = { 0 }; 
int value, n, wt[20], val[20], max_wt; 

// ---CONCERNED FUNCTION----- 

int MNSack(int i, int j) 
{ 
    value = 0; 
    if (table[i][j] < 0) 
     if (j < wt[i]) 
      value = MNSack(i - 1, j); 
     else 
      value = fmax(MNSack(i - 1, j), val[i] + MNSack(i - 1, j - wt[i])); 

    table[i][j] = value; 
    return table[i][j]; 
} 

// -------------------------- 

void items_picked(int n, int max_wt) 
{ 
    cout << "\n Items picked : " << endl; 
    while (n > 0) 
    { 
     if (table[n][max_wt] == table[n - 1][max_wt]) // if value doesnot change in table column-wise, item isn't selected 
      n--;          // n-- goes to next item 
     else           // if it changes, it is selected 
     { 
      cout << " Item " << n << endl; 
      max_wt -= wt[n];       // removing weight from total available (max_wt) 
      n--;          // next item 
     } 
    } 
} 

int main() 
{ 

    cout << " Enter the number of items : "; 
    cin >> n; 
    cout << " Enter the Maximum weight : "; 
    cin >> max_wt; 
    cout << endl; 
    for (int i = 1; i <= n; i++) 
    { 
     cout << " Enter weight and value of item " << i << " : "; 
     cin >> wt[i] >> val[i]; 
    } 

    for (int i = 0; i <= n; i++) 
     for (int j = 0; j <= max_wt; j++) 
      table[i][j] = 0; 

    for (int i = 1; i <= n; i++) 
     for (int j = 1; j <= max_wt; j++) 
      table[i][j] = -1; 

    cout << " Optimum value : " << MNSack(n, max_wt); 

    cout << " \n Table : \n"; 
    for (int i = 0; i <= n; i++) 
    { 
     for (int j = 0; j <= max_wt; j++) 
      if (table[i][j] == -1) 
       cout << setw(5) << "-"; 
      else 
       cout << setw(5) << table[i][j]; 
     cout << endl; 
    } 

    items_picked(n, max_wt); 


    return 0; 
} 

這裏的問題和輸出:
enter image description here

好像它像最佳值有些地方是正確的,但不完全可接受。 我試圖調試它,但它很難用遞歸函數。有人可以幫忙嗎?

+0

Heyho,我認爲HenryLee的回答是正確的,但它仍然希望稍後給你一些東西。 Ur代碼有點可怕,而你解決這個問題的方式在程序員之下被稱爲memoization。這是一個美麗的博客帖子,它幫助了我很多,可以讓你的代碼更加美麗。 http://programminggenin.blogspot.de/2013/01/memoization-in-c.html – Mehno

+0

@Mehno你能詳細說明嗎?據我所知,算法只計算所需的值。我能更好地實現它嗎? – LonelyC

+0

@Mehno建議的是一種讓您的編碼風格更好的技術。算法中沒有什麼不好的。但是,如果您有興趣,我可以告訴您如何使用自下而上的動態編程來解決同樣的問題,這需要很少的行數,並且使代碼更好。 – HenryLee

回答

1
int MNSack(int i, int j) 
{ 
    value = 0; 
    if (table[i][j] < 0) 
    { 
     if (j < wt[i]) 
      value = MNSack(i - 1, j); 
     else 
      value = max(MNSack(i - 1, j), val[i] + MNSack(i - 1, j - wt[i])); 

     table[i][j] = value; 
    } 
    return table[i][j]; 
} 

問題出現在這裏。當您的表格項目大於或等於0時,您將跳過遞歸,但仍將表格項目設置爲0,如果您的表格項目大於0,則表格將不正確。

您只需更新表格項目需要更改時,所以將其放在括號中可以更正此問題。

1

自下而上的解決方案。

#include <iostream> 
#include <algorithm> 
#include <iomanip> 
using namespace std; 


int main() 
{ 
    int table[20][20] = { 0 }; 
    int value, n, wt[20], val[20], max_wt; 

    cout << " Enter the number of items : "; 
    cin >> n; 
    cout << " Enter the Maximum weight : "; 
    cin >> max_wt; 
    cout << endl; 
    for (int i = 1; i <= n; i++) 
    { 
     cout << " Enter weight and value of item " << i << " : "; 
     cin >> wt[i] >> val[i]; 
    } 

    // Initialization 
    for (int i = 0; i <= n; i++) 
     for (int j = 0; j <= max_wt; j++) 
      table[i][j] = 0; 

    // In practice, this can be skipped in a bottom up solution 
    for (int i = 1; i <= n; i++) 
     for (int j = 1; j <= max_wt; j++) 
      table[i][j] = -1; 

    for (int i = 1; i <= n; i++) 
    { 
     for (int j = 1; j <= max_wt; j++) 
     { 
      if (j < wt[i]) 
       table[i][j] = table[i - 1][j]; 
      else 
       table[i][j] = max(table[i - 1][j], val[i] + table[i - 1][j - wt[i]]); 
     } 
    } 

    cout << " Optimum value : " << table[n][max_wt] << endl; 

    cout << " \n Table : \n"; 
    for (int i = 0; i <= n; i++) 
    { 
     for (int j = 0; j <= max_wt; j++) 
      if (table[i][j] == -1) 
       cout << setw(5) << "-"; 
      else 
       cout << setw(5) << table[i][j]; 
     cout << endl; 
    } 

    return 0; 
} 

您可以看到,這將遞歸更改爲循環,因此避免了全局變量。它還使代碼更簡單,以便您可以避免檢查表項是否有效(在您的示例中等於-1)。

該解決方案的缺點是,它總是遍歷所有可能的節點。但是它會獲得更好的係數,因爲遞歸和雙重檢查表項的成本更高。自頂向下和自下而上都具有相同的複雜度O(n^2),並且很難判斷哪一個更快。

+0

嘿!在探索遞歸方法之前,我實際上使用了這個確切的方法來解決這個問題。是否有可能使用遞歸來做到這一點,而不是使用全局變量? – LonelyC

+0

@LonelyC很好聽。無需全局變量就可以做到這一點,只需將變量作爲參數傳遞給每個函數即可。請注意,如果您需要更改參數的值,則必須傳遞變量的引用(或C中的指針)。在這個例子中,我們只需要改變表中的值,並且由於表是作爲指針傳遞的,所以我們不必擔心這一點。 – HenryLee

+0

哦對!我明白。感謝所有的幫助:) – LonelyC