hdu 1114Piggy-Bank(完全背包)

传送门

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
 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 }
思路2

 

猜你喜欢

转载自www.cnblogs.com/violet-acmer/p/9890254.html