zoukankan      html  css  js  c++  java
  • hdu 1114Piggy-Bank(完全背包)

    传送门

    参考资料:

      [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]×2k、价值为 p[i]×2k 的若干件物品,其中k满足 w[i]×2k < (F-E)。

        这是二进制的思想,因为不管最优策略选几件第 i 种物品,总可以表示成若干个 2k 件物品的和。

        这样把每种硬币拆成 log2( (F-E) / w[i]) ) 件物品,是一个很大的改进。

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define INF 0x3f3f3f3f
     5 const int maxn=500+50;
     6 
     7 int n;
     8 int e,f;
     9 int p[maxn],w[maxn];
    10 int dp[10010];
    11 
    12 void Solve()
    13 {
    14     int v=f-e;
    15     for(int j=1;j <= v;++j)
    16         dp[j]=INF;
    17     dp[0]=0;
    18 
    19     for(int i=1;i <= n;++i)
    20     {
    21         for(int k=0,cur=(1<<k)*w[i];cur <= v;++k)///第i个物品选2^k个
    22         {
    23             for(int j=v;j >= cur;--j)
    24                 dp[j]=min(dp[j],dp[j-cur]+(1<<k)*p[i]);
    25 
    26             cur <<= 1;
    27         }
    28     }
    29     if(dp[v] < INF)
    30         printf("The minimum amount of money in the piggy-bank is %d.
    ",dp[v]);
    31     else
    32         printf("This is impossible.
    ");
    33 }
    34 int main()
    35 {
    36     int test;
    37     scanf("%d",&test);
    38     while(test--)
    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     return 0;
    47 }
    View Code

      2.我们有更优的O(VN)的算法

        定义dp[ i ][ j ] : 前 i 件物品恰好组成重量 j 的最小面值;

        第 i 件物品的状态转移方程为:

    1 for(int j=w[i];j <= v;++j)
    2     dp[j]=min(dp[j],dp[j-w[i]]+p[i]);

        完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第 i 种物品”这种策略时,

        却正需要一个可能已选入第 i 种物品的子结果 dp[ i ][ v-w[i] ],所以就可以并且必须采用 j = w[i]..v 的顺序循环。

        以上思路摘抄自[1]%%%%%%%%%%%%%%%

    AC代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define INF 0x3f3f3f3f
     5 const int maxn=500+50;
     6 
     7 int n;
     8 int e,f;
     9 int p[maxn],w[maxn];
    10 int dp[10010];
    11 
    12 void Solve()
    13 {
    14     int v=f-e;
    15     for(int j=1;j <= v;++j)
    16         dp[j]=INF;
    17     dp[0]=0;
    18 
    19     for(int i=1;i <= n;++i)
    20         for(int j=w[i];j <= v;++j)
    21             dp[j]=min(dp[j],dp[j-w[i]]+p[i]);
    22 
    23     if(dp[v] < INF)
    24         printf("The minimum amount of money in the piggy-bank is %d.
    ",dp[v]);
    25     else
    26         printf("This is impossible.
    ");
    27 }
    28 int main()
    29 {
    30     int test;
    31     scanf("%d",&test);
    32     while(test--)
    33     {
    34         scanf("%d%d",&e,&f);
    35         scanf("%d",&n);
    36         for(int i=1;i <= n;++i)
    37             scanf("%d%d",p+i,w+i);
    38         Solve();
    39     }
    40     return 0;
    41 }
    View Code
  • 相关阅读:
    leetcode刷题 650~
    leetcode刷题 633~
    发送udp报文
    SIP (Session Initiation Protocol) 协议
    你所不知道的replace
    uni-app(六)生成海报图片路径问题
    uni-app(五)小程序的一些注意事项,踩坑
    uni-app(四)小程序里的vuex
    uni-app(三)组件、插件使用,引入字体
    uni-app(二)接口请求封装,全局输出api
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/9890254.html
Copyright © 2011-2022 走看看