目前看到的01背包问题都不是递归方法解决的,应该是数量+容量的组合造成的特殊性质?
重点是理解状态转移方程。容量够了,可以选择放不放,如果放了会占据一定的容量,而剩余容量用来放其他的。
另外还有一个延申的问题,回溯所选组合。这里是逆序回溯,从最终状态dp[num][c]开始,用最大容量和物品[num-1]所需的容量比较,确定能放入再进行判断:是否有放入?有无放入物品i就看除去物品[num-1]之后的收益是否等于放了之后剩余容量的最大收益。其实就是上面的思路
template<class T>
class my_2DAry {
public:
//typedef typename T value_type;
T** pp;
int m_l, m_c;
my_2DAry(int l, int c):m_l(l), m_c(c) {
pp = new T* [l];
for (int i = 0; i < l; i++)pp[i] = new T[c];
}
~my_2DAry() {
if (pp != nullptr) {
for (int i = 0; i < m_l; i++) {
if (pp[i] != nullptr) {
delete[] pp[i];
pp[i] = nullptr;
}
}
delete[] pp;
pp = nullptr;
}
//cout << "my_2DAry destruct done" << endl;
}
void print() {
for (int i = 0; i < m_l; i++) {
for (int j = 0; j < m_c; j++)cout << pp[i][j] << ' ';
cout << endl;
}
}
bool setElem(int l, int c, int e) {
if (l > m_l || c > m_c)return false;
pp[l][c] = e;
return true;
}
void init(T e) {
for (int i = 0; i < m_l; i++)
memset(pp[i], e, m_c*sizeof(T));
}
};
int bag(int* w, int* p, int num, int c) {
if (!w || !p || !num || !c)return 0;
my_2DAry<int>dp(num + 1, c + 1);//为了方便计算dp中i-1=0的场景 //注意w[]和p[]从num=1开始
dp.init(0);
for (int i = 1; i <= num; i++) {
for (int j = 1; j <= c; j++) {
if (j < w[i - 1])dp.pp[i][j] = dp.pp[i - 1][j];//i-1可能为0
else dp.pp[i][j] = std::max(dp.pp[i - 1][j], dp.pp[i - 1][j - w[i - 1]] + p[i - 1]);
}
}
int* item = new int[num];
auto func = [&dp, &w, &p, &item](int num,int c)->void{
for (int i = num; i > 0; i--) {
if (dp.pp[i][c] == dp.pp[i - 1][c])item[i - 1] = 0;
else if (c - w[i - 1] >= 0 && dp.pp[i][c] == dp.pp[i - 1][c - w[i - 1]] + p[i - 1]) {
item[i - 1] = 1;
c -= w[i - 1];
}
}
};//lambda也会析构以复制形式捕获的类,注意里面的指针成员
func(num, c);
for (int i = 0; i < num; i++)cout << item[i] << ' '; cout << endl;
delete[] item;
return dp.pp[num][c];
}
void test02() {
int n = 4;
int c = 8;
int w[] = { 2 , 3 , 4 , 5 };
int p[] = { 3 , 4 , 5 , 6 };
cout << bag(w, p, n, c) << endl;
}