完全背包问题是一个很经典的动态规划问题
有n中价值和重量分别为wi, vi的物品, 从中选任意数量总重量不超过m的物品, 使得总价值最大, 每种物品可以挑任意次
直观思考的话, 由01背包问题来思考, 引入一个遍历k, 可得到以下状态转移方程
dp[i][j] = max{dp[i-1][j-w[i]*k] + k*v[i] | j >= w[i]*k } (0 <= k < n)
int solve()
{
ms(dp, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
for(int k = 0; j >= k*w[i]; k++)
dp[i][j] = max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
}
return dp[n][m];
}
但复杂度太高了, 我们仔细思考, 其实存在了大量的重复计算, 可简化为以下
int solve()
{
ms(dp, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
if(j < w[i])
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]]+v[i]); //避免重复计算
}
return dp[n][m];
}
如果题目对空间复杂度有要求的话, 还可以优化一下空间复杂度~
int solve()
{
ms(dp, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
if(j >= w[i])
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
return dp[m];
}
下面来说这道题, 其实就是一个标准的完全背包问题, 只不过由取最大变成了取最小, max改成min就好了, 注意把边界设置为无穷大, 然后再判断一下是否有解即可
//完全背包问题
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const LL maxm = 10010, maxn = 510;
int n, m, v[maxn], w[maxn];
int dp[maxm];
void solve()
{
for(int i = 0; i < maxm; i++)
dp[i] = 1<<30;
dp[0] = 0;
for(int i = 1; i <= n; i++)
for(int j = w[i]; j <= m; j++)
dp[j] = min(dp[j], dp[j-w[i]]+v[i]);
if(dp[m] != (1<<30))
printf("The minimum amount of money in the piggy-bank is %d.\n",dp[m]);
else
printf("This is impossible.\n");
}
int main()
{
int T, e, f;
cin >> T;
while(T--){
cin >> e >> f;
m = f-e; //背包的容量
cin >> n;
for(int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
solve();
}
return 0;
}