Piggy-Bank
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 13355 Accepted Submission(s): 6753
But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!
3 10 110 2 1 1 30 50 10 110 2 1 1 50 30 1 6 2 10 3 20 4
The minimum amount of money in the piggy-bank is 60. The minimum amount of money in the piggy-bank is 100. This is impossible.
解题报告:
这题是一题典型的完全背包恰好装满背包的最小价值的题,背包9讲里面写得很清楚,直接在初始化的时候做文章。代码也没有很特殊的地方。
先附上代码:
#include <stdio.h>
#include <string.h>
#define min(a, b) (a < b ? a : b)
int main()
{
int t, i, j, W, n, dp[10005], w, v;
scanf("%d", &t);
while(t--){
memset(dp, 0x7f, sizeof(dp)), dp[0] = 0;
scanf("%d%d%d", &i, &j, &n);
W = j - i;
for (i = 0; i < n; i ++){
scanf("%d%d", &v, &w);
for (j = w; j <= W;j ++){
dp[j] = min(dp[j], dp[j - w] + v);
}
}
if(dp[W] < 0x7f7f7f7f)
printf("The minimum amount of money in the piggy-bank is %d.\n", dp[W]);
else
printf("This is impossible.\n");
}
return 0;
}
现在我们来解释为什么是这么初始化dp的,dp[i] = inf, dp[0] = 0。
dp[0] = 0好理解,装满没容量的背包的价值最小且刚好为0.
继续明白一个问题,只有当dp[j-w[i]可以]装满的时候,dp[j]才可能装满。
递推式:dp[j]=min(dp[j],dp[j-w[i]]+p[i]);
滚动数组,就不多介绍了。等号左边和右边都是“d[i][*]的二维数组”(注意这是完全背包)。
如果dp[j-w[i]]暂时不可能装满, 分两类:
如果dp[j]已经可能装满(前几轮得到的):dp[j] = dp[j];
如果dp[j]暂时不可能装满:dp[j] = min(inf,inf+p[i]) = inf = dp[j];
如果dp[j-w[i]]已经可能装满, 分两类:
如果dp[j]已经可能装满(前几轮得到的):dp[j]=min(dp[j],dp[j-w[i]]+p[i]);
如果dp[j]暂时不可能装满:dp[j] = dp[j-w[i]]+p[i];
无论是哪种情况,都满足dp[j]=min(dp[j],dp[j-w[i]]+p[i]);
已经可能装满条件是dp[i] < inf;
但inf存在取值问题,需要考虑。
这题的储蓄罐最大值不超过10000,假设只有一种钱币,w = 1, v = 50000. 最大价值达到10000*50000 = 5e8,
inf要取比这个大才行,避免出现可以装满dp[i] >= inf。
顺便把装满整个背包的最大价值为什么dp[i] = -inf, dp[0] = 0也解释下:
dp[0] = 0不用解释。
如果dp[j-w[i]]不可达,分两类:
dp[j]可达,dp[j] = dp[j] < 0;
dp[j]不可达,dp[j] = dp[j-w[i] + p[i]] < 0;
如果dp[j-w[i]]可达,分两类:
dp[j]可达,dp[j] = max(dp[j],dp[j-w[i]]+p[i]) > 0 ;
dp[j]不可达,dp[j] = dp[j-w[i] + p[i]] > 0;
无论是哪种情况,都满足 dp[j]=max(dp[j],dp[j-w[i]]+p[i]);
可达的条件是dp[j] > 0;需要-inf足够小,不会通过有限次加p[i]变为正值,即不可达dp[i] > 0。
这些都要自己先判断,最好的办法是取int的最大值。