这里推荐灵神的讲解:动态规划入门:从记忆化搜索到递推
思考过程为:递归->记忆化搜索->递推
题目:何以包邮?
很显然这是一个背包问题,但是又并不是那么直接的背包问题,目的是找出一个组合(每个元素只能选取一次,0-1背包)且该组合的和是大于包邮条件数值集合里面最小的那个数。这里需要转换一下思维,首先假设所有书的总价格为sumValue
,那么其实我们的target
是sumValue - x
,即找到不大于sumValue - x
最大值,那么剩下的书的总价格必然是大于包邮条件的最小值。
递归公式:
d
f
s
(
i
,
c
)
=
m
a
x
(
d
f
s
(
i
−
1
,
c
)
,
d
f
s
(
i
−
1
,
c
−
b
o
o
k
[
i
]
)
+
b
o
o
k
[
i
]
)
dfs(i, c) = max(dfs(i - 1, c), dfs(i-1, c - book[i]) + book[i])
dfs(i,c)=max(dfs(i−1,c),dfs(i−1,c−book[i])+book[i]) 这是一个从后向前的过程。
意思就是当我还有c的容量且考虑是否装下book[i]的时候,它应该是在【不买该书,买该书】之中取一个最大值。
#include <bits/stdc++.h>
using namespace std;
int n, x;
int dfs(int i, int c, vector<int> &books, vector<vector<int>> &cache)
{
if (i < 0)
return 0;
int &tmp = cache[i][c];
if (tmp != -1)
return tmp;
if (c < books[i])
return tmp = dfs(i - 1, c, books, cache);
return tmp = max(dfs(i - 1, c, books, cache), dfs(i - 1, c - books[i], books, cache) + books[i]);
}
int main(int argc, char **argv)
{
cin >> n >> x;
vector<int> book(n, 0);
int sumValue = 0;
for (int i = 0; i < n; i++)
{
cin >> book[i];
sumValue += book[i];
}
int target = sumValue - x;
vector<vector<int>> cache(n + 1, vector<int>(target + 1, -1));
int res = dfs(n - 1, target, book, cache);
cout << sumValue - res << endl;
return 0;
}
转化为递推式(从前往后):
#include <bits/stdc++.h>
using namespace std;
int n, x;
int main(int argc, char** argv)
{
cin >> n >> x;
vector<int> book(n, 0);
int sumValue = 0;
for (int i = 0; i < n; i++)
{
cin >> book[i];
sumValue += book[i];
}
int target = sumValue - x;
vector<vector<int>> res(n + 1, vector<int>(target + 1, 0));
for (int i = 0; i < n; i++)
for (int c = 0; c < target + 1; c++)
{
if (c < book[i])
res[i + 1][c] = res[i][c];
else
res[i + 1][c] = max(res[i][c], res[i][c - book[i]] + book[i]);
}
cout << sumValue - res[n][target] << endl;
return 0;
}