zoukankan      html  css  js  c++  java
  • HDOJ 1114 Piggy-Bank 【动态规划 完全背包】

    题目链接:

      http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17679


    题目描述:

      一个原重量为e,装满重量为f的小猪存钱罐,有n种硬币,每种货币有value和weight,每种硬币可以无限次选用

      要求输出使重量符合要求的,价值最小的组合方式,

      如果没有符合重量要求的硬币组合,输出impossible


    问题模型:

      完全背包,只不过因为要求价值最小,所以将状态方程的max换成min


    初始化的细节问题:

      我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。

      一种区别这两种问法的实现方法是在初始化的时候有所不同。

      如果是第一种问法,要求恰好装满背包,那么在初始化时除了f[0]为0其它f[1..V]均设为-∞,这样就可以保证最终得到的f[N]是一种恰好装满背包的最优解。

      如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将f[0..V]全部设为0。

      为什么呢?可以这样理解:

      初始化的f数组事实上就是在没有任何物品可以放入背包时的合法状态。

      如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-∞了。

      如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。


    完全背包:

      有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

      这个问题非常类似于01背包问题,所不同的是每种物品有无限件。

      也就是从每种物品的角度考虑,与它相关的策略已并非取或不取两种,而是有取0件、取1件、取2件……等很多种。

      如果仍然按照解 01背包时的思路,令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程,像这样:

    f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}

    o(vn)的算法:

      这个算法使用一维数组,先看伪代码:

    for i=1..N
        for v=0..V
            f[v]=max{f[v],f[v-cost]+weight}

      你会发现,这个伪代码与P01的伪代码只有v的循环次序不同而已。

      为什么这样一改就可行呢?

      首先想想为什么P01中要按照v=V..0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1] [v-c[i]]递推而来。

      换句话说,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。

      而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v=0..V的顺序循环。这就是这个简单的程序为何成立的道理。

    这个算法也可以以另外的思路得出。

      将基本思路中求解f[i][v-c[i]]的状态转移方程显式地写出来,代入原方程中:

      f[i][v]=f[i-1][v-k*c[i]]+k*w[i]

      f[i][v-c[i]]=f[i-1][v-c[i]-(k-1)*c[i]]+(k-1)*w[i]

                  =f[i-1][v-k*c[i]]+k*w[i] - w[i]

      所以,

    f[i][v]=f[i][v-c[i]]+w[i]

      将这个方程用一维数组实现,便得到了上面的伪代码。

    最后抽象出处理一件完全背包类物品的过程伪代码:

    procedure CompletePack(cost,weight)
        for v=cost..V
            f[v]=max{f[v],f[v-c[i]]+w[i]}

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 const int INF = 0x3f3f3f3f;
     6 const int MAXL = 500+5;
     7 int t, e, f, v, n, value[MAXL], weight[MAXL], dp[10005];
     8 int min(int a, int b) { return a < b ? a : b; }
     9 
    10 int main(){
    11     scanf("%d", &t);
    12     while(t--){
    13         scanf("%d%d", &e, &f);
    14         v = f-e;
    15         scanf("%d", &n);
    16         //---初始化
    17         dp[0] = 0;
    18         for(int i = 1; i <= v; i++) dp[i] = INF;
    19         //---
    20         for(int i = 0; i < n; i++) scanf("%d%d", &value[i], &weight[i]);
    21         for(int i = 0; i < n; i++){
    22             for(int j = weight[i]; j <= v; j++){
    23                     dp[j] = min(dp[j-weight[i]]+value[i], dp[j]);
    24             }
    25         }
    26         if(dp[v] == INF) printf("This is impossible.
    ");
    27         else printf("The minimum amount of money in the piggy-bank is %d.
    ", dp[v]);
    28     }
    29 
    30     return 0;
    31 }

     

  • 相关阅读:
    postman的使用
    测试模型的发展论
    winform 控件没有Cursor属性时的处理办法
    【C#】两个DataTable关联查询(inner join、left join)C#代码
    【C#】datetimepicker里面如何设置日期为当天日期,而时间设为0:00或23:59?
    【C#】string格式的日期转为DateTime类型及时间格式化处理方法
    js解析后台传过来的json
    shell做成csv文件
    Hibernate查询总的记录数
    在引入的css或者js文件后面加参数的作用
  • 原文地址:https://www.cnblogs.com/miaowTracy/p/4873368.html
Copyright © 2011-2022 走看看