1.题目链接。题目说了很多,但是意思就是给你一个储蓄罐,空着的时候的重量和最多能够承受的重量,然后给你一些硬币,这些硬币有价值和重量,找出把这个储蓄罐装满的时候硬币加起来最小的值。
2.这很类似01背包的问题,但是也有区别。我们知道在01背包中每个物品只有一个,但是这里的硬币(物品)无限多。我们把这种问题叫做完全背包。
3.完全背包我们需要考虑的问题不再是这种物品拿与不拿,而是拿几个的问题。所以DP方程很好写:
但是我们这里是最小值,所以就是min了。最后我们需要注意一个问题:就是内层循环的顺序,在01中,我们是从V到V[i],这是为什么呢?从两个角度来理解这个问题。我们之所以在枚举容量的时候是从大到小枚举,(1)还记得记忆化搜索吧,我们如果用dfs来处理这个问题的时候,我们的dfs(int n,int cur).带了两个参数,第一个就是物品的数量,第二个就是剩下的容量,所以我们在DP的时候,容量肯定是从大到小枚举的。(2)我们为什么从大到小枚举呢?难道就是因为我们dfs写的就是那个样子吗?更加深层次的原因,是因为我们害怕重复!因为01背包中每种物品最多被用一次,所以我们从V-V[i]这样枚举下来,可以保证用过的一定不会再用。但是在我们今天的这个问题上,显然不需要这样做,所以我们的循环是正着的。从V[i]到V。知道了这些,代码就简单了。下边是AC代码:
using namespace std;
const int n = 1e5 + 10;
int dp[n], w[n], v[n];
int main()
{
int T;
int E, F;
int W;
scanf("%d", &T);
int N;
while (T--)
{
scanf("%d%d", &E, &F);
W = F - E;//容量
scanf("%d", &N);
for (int i = 0; i < N; i++)
{
scanf("%d%d", &v[i], &w[i]);
}
for (int i = 0; i <= W; i++)
{
dp[i] = inf;
}
dp[0] = 0;
for (int i = 0; i < N; i++)
{
for (int j = w[i]; j <= W; j++)
{
dp[j] = min(dp[j], dp[j - w[i]] + v[i]);
}
}
if (dp[W]<inf)
{
printf("The minimum amount of money in the piggy-bank is %d.\n", dp[W]);
}
else
{
printf("This is impossible.\n");
}
}
return 0;
}