题意大概是给出储钱罐的最小重量e和最大重量f,再给出钱币的重量和价值,求重量最大时的最小价值。
完全背包问题,不解释,套模板吧。
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxint 100000000
using namespace std;
int minx(int i,int j)
{
return i<j?i:j;
}
int main()
{
int t,e,f,n;
int weight[502],value[502],dp[10005];
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&e,&f,&n);
for(int i=1;i<=f-e;i++)
dp[i]=maxint;
dp[0]=0;
for(int i=0;i<n;i++)
scanf("%d%d",&value[i],&weight[i]);
for(int i=0;i<n;i++)
{
for(int j=weight[i];j<=f-e;j++)
{
dp[j]=minx(dp[j],dp[j-weight[i]]+value[i]);
//printf("(%d,%d) ",dp[j],j);
}
//printf("\n");
}
if(dp[f-e]==maxint)
printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",dp[f-e]);
}
return 0;
}
以下是一些经过辛苦思考后的检验:
完全背包关键代码:
for(int i=0;i<n;i++)
{
for(int j=weight[i];j<=f-e;j++)
{
dp[j]=minx(dp[j],dp[j-weight[i]]+value[i]);
printf("(%d,%d) ",dp[j],j);
}
printf("\n");
}
输入如下:
1
10 19
2
2 4
1 1
输出为:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
1 | ∞ | ∞ | ∞ | 2 | ∞ | ∞ | ∞ | 4 | ∞ |
2 | 1 | 2 | 3 | 2(4) | 3 | 4 | 5 | 4(6) | 5 |
由表可见第1行(2 ,4) 用了两次,第2行第4列本来为4但由于2<4所以为2,后面类似。第8列就是(2,4)两次、(1,1)一次。
01背包关键代码:
for(int i=0;i<n;i++)
{
for(int j=f-e;j>=weight[i];j--)
{
dp[j]=maxn(dp[j],dp[j-weight[i]]+value[i]);
printf("(%d,%d) ",dp[j],j);
}
printf("\n");
}
输入如下:
3
10 19
4
2 8
3 4
2 5
4 1
输出如下
9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
|
2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
3 | 3 | 3 | 3 | 3 | 3 | 0 | 0 | 0 | 2 |
5 | 3 | 3 | 3 | 3 | 3 | 0 | 0 | 0 | 3 |
7 | 7 | 7 | 7 | 7 | 4 | 4 | 4 | 4 | 4 |
由于j是递减的,且数组初始化为0,所以第1行可以看出(2,8)只用了一次,第2行只用(3,4)一次,第3行第9列使用(3,4)(2,5)各一次。。。
可见for循环逆序推能够保证 f[v-c[i]] 保存的是状态是 f[i-1][v-c[i]] ,也就是每个物品只被使用了一次;顺序的话 f[v-c[i]] 保存的是 f[i][v-c[i]] ,每个物品有可能被使用多次,也就是完全背包问题的解法。
初始化问题:(选自背包九讲)如果是01背包要求恰好装满背包,则dp[0]=0,其余初始化为-∞;如果没有要求背包恰好装满,只是希望得到的价值最大,则全初始化为0。