https://www.cnblogs.com/violet-acmer/p/9852294.html
参考资料:
[1]:https://www.cnblogs.com/jbelial/articles/2116074.html
[2]:https://www.luogu.org/problemnew/solution/P1616
题意:
有一个小猪存钱罐,里面有各式各样的硬币,每种硬币有不同的面值和重量。
现测量出存钱罐初始的重量(E)和存满钱时的重量(F),问在这N种硬币中,如何选取,使得选取的硬币的总重量恰好等于(F-E),且总价值最小。
如果可以找到,输出"The minimum amount of money in the piggy-bank is num".(num : 总和最小的面值)
如果不能通过组合使得硬币总重量恰好等于(F-E),则输出"This is impossible."
分析:
乍一看,和“01”背包很想,所不同的是,在“01”背包中,第 i 种物品只有两种选择,拿或者不拿,而此题,第 i 种面值的硬币可不止有拿与不拿这两种选择,而是有拿
0,1,2,.....,k个,共(k+1)种选择,其中k满足 k*w[i] <= (F-E);
这种每种物品都有无限件可用的问题,成为“完全背包”问题。
题解:
1.完全背包转“01”背包
思路:将一种物品拆成多件物品。
考虑到第 i 种物品最多选 (F-E) / w[ i ] 件,于是可以把第 i 种物品转化为 (F-E) / w[ i ] 件费用及价值均不变的物品,然后求解这个01背包问题。
如果开个二维的dp数组,指定不可行,具体为什么,请自行思考?
提示:假设每种硬币都可拆成 k 个(k 最大可达 10000),N种硬币则可拆成 N*k 个(N最大为500),所以最坏的情况是可拆成 N*k = 5e6 个物品。
比较好用的方法就是使用滚动数组优化空间。
更高效的转化方法是:把第i种物品拆成重量为 w[i]*(2^k)、价值为 p[i]*(2^k) 的若干件物品,其中k满足 w[i]*(2^k) < (F-E)。
这是二进制的思想,因为不管最优策略选几件第 i 种物品,总可以表示成若干个 2^k 件物品的和。
这样把每种硬币拆成 log( (F-E)/w[i]))(此处表示的是以2为底的对数)件物品,是一个很大的改进。
2.我们有更优的O(VN)的算法
详情请看参考资料[1]
以上思路参考自大神思路%%%%%%%%%%%%%%%
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 const int maxn=10000+50; 9 10 int E,F; 11 int N; 12 int p[550],w[550]; 13 int dp[maxn]; 14 15 void Solve() 16 { 17 mem(dp,INF); 18 dp[0]=0; 19 for(int i=1;i <= N;++i) 20 { 21 int k=log(1.0*(F-E)/w[i])/log(2.0);//第 i 种硬币最多选 k 个 22 for(int j=0;j <= k;++j) 23 { 24 int g=(1<<j)*w[i];//第 i 种硬币选 2^j 个的总重量 25 for(int l=F-E;l >= g;--l) 26 dp[l]=min(dp[l],dp[l-g]+(1<<j)*p[i]); 27 } 28 } 29 if(dp[F-E] != INF) 30 printf("The minimum amount of money in the piggy-bank is %d.\n",dp[F-E]); 31 else 32 printf("This is impossible.\n"); 33 } 34 int main() 35 { 36 int T; 37 scanf("%d",&T); 38 while(T--) 39 { 40 scanf("%d%d",&E,&F); 41 scanf("%d",&N); 42 for(int i=1;i <= N;++i) 43 scanf("%d%d",p+i,w+i); 44 Solve(); 45 } 46 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #define mem(a,b) memset(a,b,sizeof(a)) 8 const int maxn=10000+50; 9 10 int E,F; 11 int N; 12 int p[550],w[550]; 13 int dp[maxn]; 14 15 void Solve() 16 { 17 mem(dp,INF); 18 dp[0]=0; 19 for(int i=1;i <= N;++i) 20 for(int j=w[i];j <= (F-E);++j) 21 dp[j]=min(dp[j],dp[j-w[i]]+p[i]); 22 if(dp[F-E] != INF) 23 printf("The minimum amount of money in the piggy-bank is %d.\n",dp[F-E]); 24 else 25 printf("This is impossible.\n"); 26 } 27 int main() 28 { 29 int T; 30 scanf("%d",&T); 31 while(T--) 32 { 33 scanf("%d%d",&E,&F); 34 scanf("%d",&N); 35 for(int i=1;i <= N;++i) 36 scanf("%d%d",p+i,w+i); 37 Solve(); 38 } 39 }