for 所有的组k
forv=V..0
for 所有的i属于组k
f[v]=max{f[v],f[v-c[i]]+w[i]}
为了符合本题目至少选一个的目的,感觉应该将上面第二个循环和第三个循环调整顺序,方可遍历本组选1,2....个物品的目的。有了以上的代码框架然后就是寻找状态方程了。有题目可得到:需要实现本组至少选择一个,因此需要遍历本组;需要从上一个分组传传递过俩,因此需要遍历上一个分组;
如果采用01背包的解题框架直接利用f[j]=max(f[j],g[j-b[i]]+c[i]);来合并以上两个决策,则会使题目变成真正的01背包了(因为在处理下一个分组的第一个元素的f[资源上限]时候用的是上一个分组整体处理之后的结果,如果该值很大,则会使第一个分组的该最大值一直保存到整体处理完成,而该处理方式正式01背包的处理方式,即该值不是在本资源限制条件下取本元素之后算出来的)。因此,应该将以上两种决策分开来计算,并且能够至少存储上一个分组的计算结果(即可以对每一个分组开一个数据,保存全部的中间结果;也可以只用两个滚动数组,保存上一个分组的结果和本分组的结果)。本文采用第一种表示方式,比较清晰易懂;得到状态转移方程如下所示:
f[j][v]= max(f[j][v], f[j][v-cost]+value);//表示在本组遍历
f[j][v]= max(f[j][v], f[j-1][v-cost]+value);//表示在上一个分组遍历
并且第一个方程必须在第二方程前面,因为商品存在0花费的情况,如果调换位置当花费为0使,会使该商品在两个方程的的个参数中计算两次造成重复。
在对f数组初始化的时候,应该考虑到商品存在花费为0的情况,故不能初始化为0,否则在出结果为0的时候,会无法分清楚是初始化的还是程序计算的,没有可比较的初始值,那就需要自己再添加变量记录会很麻烦。因此,dp[][]数组初始化为-1表示所有的数都不合法。大于0表示合法,然后将所有的 k = 0 dp[0][]置为0,这是为了 k = 1时能合法计算。在状态转移的时候应该考虑方程第二个参数的状态是否合法,因此得到具体代码为:
if(dp[k][j - m[k][i].m] != -1)
dp[k][j] = Max(dp[k][j] , dp[k][j - m[k][i].m] + m[k][i].v);
if(dp[k-1][j - m[k][i].m] != -1 )
dp[k][j] = Max(dp[k][j] , dp[k-1][j - m[k][i].m] + m[k][i].v);
经过整理程序的解题过程如下所示:
#include <iostream> #include <vector> using namespace std; vector<pair<long,long> > coll3033[11]; long f3033[11][10001]; void init() { for(int i=0;i<11;++i) coll3033[i].clear(); memset(f3033,-1,sizeof(f3033)); memset(f3033[0],0,sizeof(f3033[0]));//为了使第一组商品可以合法计算 } int main() { int N,M,K; long ta=0,tb=0,tc=0; while(cin>>N>>M>>K) { init(); while(N--) { cin>>ta>>tb>>tc; coll3033[ta].push_back(make_pair(tb,tc)); } for(int i=1;i<=K;++i)//所有分组 for(int k=0;k<coll3033[i].size();++k)//每个分组中的商品; for(int j=M;j>=coll3033[i][k].first;--j)//价格限制;调欢分组背包中这两句位置,是为了实现每组最少选一个,否则就是最多选一个 { if(f3033[i][j-coll3033[i][k].first]!=-1)//如果不加这两个if就是对N件商品进行01背包,因为第i行初始化为-1 f3033[i][j]=max(f3033[i][j],f3033[i][j-coll3033[i][k].first]+coll3033[i][k].second);//所以本句是否不起作用 if(f3033[i-1][j-coll3033[i][k].first]!=-1)//注意这里的2个if的顺序,如果上面面这个if在上面的话,当体积为0时,相当于,Max( dp[k][j], dp[k][j] + m[k][i].v); dp[k][j]的值很可能会被计算两次。比如我最后给的那个测试例子。输出会是3,不会是正确答案2. f3033[i][j]=max(f3033[i][j],f3033[i-1][j-coll3033[i][k].first]+coll3033[i][k].second); } if(f3033[K][M]>=0) cout<<f3033[K][M]<<endl; else cout<<"Impossible"<<endl; } return 0; }