Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3984 Accepted Submission(s): 1763
二维费用的背包问题是指:对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有 一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和 b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。
费用加了一维,只需状态也加一维即可。设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}
如前述方法,可以只使用二维的数组:当每件物品只可以取一次时变量v和u采用逆序的循环,当物品有如完全背包问题时采用顺序的循环。当物品有如多重背包问题时拆分物品。这里就不再给出伪代码了,相信有了前面的基础,你能够自己实现出这个问题的程序。
有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取M件物品。这事实上相当于每件物品多了一种“件数 ”的费用,每个物品的件数费用均为1,可以付出的最大件数费用为M。换句话说,设f[v][m]表示付出费用v、最多选m件时可得到的最大价值,则根据物 品的类型(01、完全、多重)用不同的方法循环更新,最后在f[0..V][0..M]范围内寻找答案。
另一种看待二维背包问题的思路是:将它看待成复数域上的背包问题。也就是说,背包的容量以及每件物品的费用都是一个复 数。而常见的一维背包问题则是实数域上的背包问题。(注意:上面的话其实不严谨,因为事实上我们处理的都只是整数而已。)所以说,一维背包的种种思想方法,往往可以应用于二位背包问题的求解中,因为只是数域扩大了而已。
作为这种思想的练习,你可以尝试将P11中提到的“子集和问题”扩展到复数域(即二维),并试图用同样的复杂度解决。
当发现由熟悉的动态规划题目变形得来的题目时,在原来的状态中加一纬以满足新的限制是一种比较通用的方法。希望你能从本讲中初步体会到这种方法。
code:
1 #include <iostream> 2 #include <iomanip> 3 #include <fstream> 4 #include <sstream> 5 #include <algorithm> 6 #include <string> 7 #include <set> 8 #include <utility> 9 #include <queue> 10 #include <stack> 11 #include <list> 12 #include <vector> 13 #include <cstdio> 14 #include <cstdlib> 15 #include <cstring> 16 #include <cmath> 17 #include <ctime> 18 #include <ctype.h> 19 using namespace std; 20 21 #define MAXN 110 22 23 int n,m,k1,s; 24 25 typedef struct 26 { 27 int jy,rn; 28 }node; 29 node monster[MAXN]; 30 31 int dp[2010][2010]; 32 //dp[i][j]表示在忍耐度为i,杀怪数量为j的情况下,获取最大的经验值 33 34 int main() 35 { 36 int i,j,k; 37 while(~scanf("%d%d%d%d",&n,&m,&k1,&s)) //还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数 38 { 39 memset(dp,0,sizeof(dp)); 40 for(i=0;i<k1;i++) 41 { 42 scanf("%d%d",&monster[i].jy,&monster[i].rn); 43 for(j=monster[i].rn;j<=m;j++) //忍耐度 44 for(k=1;k<=s;k++) //怪的数量 45 { 46 dp[j][k]=max(dp[j][k],dp[j-monster[i].rn][k-1]+monster[i].jy); 47 } 48 } 49 if(dp[m][s]>=n) 50 { 51 for(i=0;i<=m;i++) 52 { 53 if(dp[i][s]>=n) 54 break; 55 } 56 printf("%d\n",m-i); 57 } 58 else 59 printf("-1\n"); 60 } 61 return 0; 62 }