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.\n");
27         else printf("The minimum amount of money in the piggy-bank is %d.\n", dp[v]);
28     }
29 
30     return 0;
31 }

 

 

posted @ 2015-10-13 00:51  快扶哀家去刷题  阅读(174)  评论(0编辑  收藏  举报