使用递归算法求解背包
Posted
技术标签:
【中文标题】使用递归算法求解背包【英文标题】:Solving Knapsack using recursive algorithm 【发布时间】:2016-03-29 14:59:35 【问题描述】:所以,我正在尝试从我们的教科书中实现这个算法。
我写了这个:
// 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;
这是问题和输出:
在某些地方,例如最优值,它似乎是正确的,但并不完全可以接受。 我试过调试它,但递归函数很难。有人可以帮忙吗?
【问题讨论】:
嘿嘿嘿,我觉得HenryLee的回答是对的,不过还是想给你点东西留着。你的代码有点糟糕,你解决这个问题的方式在程序员下被称为记忆化。这是一篇漂亮的博文,对我帮助很大,可以让你的代码更漂亮。 programminggenin.blogspot.de/2013/01/memoization-in-c.html @Mehno 你能详细说明一下吗?据我了解,该算法仅计算所需的值。我怎样才能更好地实现它? @Mehno 建议的是一种让你的编码风格更好的技术。你的算法没有什么不好的。但是,如果您有兴趣,我可以向您展示如何使用自下而上的动态规划来解决相同的问题,它只需要很少的行数,并且使代码更好。 @HenryLee 正确。算法很棒。我只是认为,对矩阵使用全局变量有点难看。没有什么不好的。我只是认为指出这种技术会有所帮助。 @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,这将是不对的。
您只需要在需要更改时更新表格项,因此将其放在大括号中即可解决此问题。
【讨论】:
【参考方案2】:自下而上的解决方案。
#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),很难说哪个更快。
【讨论】:
嘿!在探索递归方法之前,我实际上使用了这种精确的方法来解决问题。是否可以使用递归而不使用全局变量来做到这一点? @LonelyC 很高兴听到这个消息。在没有全局变量的情况下总是可以做到这一点,只需将变量作为参数传递给每个函数。请注意,如果您需要更改参数的值,则必须改为传递变量的引用(或 C 中的指针)。在这个例子中,我们只需要改变表中的值,并且由于表是作为指针传递的,所以我们不必担心。以上是关于使用递归算法求解背包的主要内容,如果未能解决你的问题,请参考以下文章