题意
n类工作,每类有m个独立的工作和属性s,s有必做一个,最多只能做一个和任意选择这三个属性.每个独立的工作有耗时和收益两个属性.求在t时间内的最大收益.
分析
令(dp[i][j])表示在前i类任务j耗时内的最大收益.若不加属性s,就是一个单纯的分组背包.现在对不同的s,有不同的状态转移方程.
1.该类必选.
(dp[i][j] = max(dp[i][j],dp[i-1][j-w]+v,dp[i][j-w]+v)),其中(dp[i][j]) 表示不选,我们不想让它不选,所以将(dp[i])全部初始化为-INF,这样状态就会优先从已选物品的状态转移过来.
2.最多选一个
相当于对这类物品做一次0-1背包,但是状态是在之前一层基础上的.向将前一层的值赋给当前层.再做状态转移:(dp[i][j] = max(dp[i][j], dp[i-1][j-w]+v))
3.任意选择
此时仍有状态转移:(dp[i][j] = max(dp[i][j],dp[i-1][j-w]+v,dp[i][j-w]+v)),其中(dp[i][j]).也是需要将上一层的dp值覆盖到当前层.
#include<bits/stdc++.h>
#define PII pair<int,int>
#define MP make_pair
#define X first
#define Y second
using namespace std;
const int maxn = 1e2+5;
const int INF = 0x3f3f3f3f;
typedef long long LL;
int dp[maxn][maxn];
vector<PII> vz[maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int n,W;
while(scanf("%d %d",&n, &W)==2){
memset(dp,0,sizeof(dp));
for(int i=0;i<maxn;++i) vz[i].clear();
int sum = 0;
for(int i=1,m,s;i<=n;++i){
scanf("%d %d",&m, &s);
while(m--){
int c,g;
scanf("%d %d",&c, &g);
vz[i].push_back(MP(c,g));
}
if(s==0){
memset(dp[i],-INF,sizeof(dp[i]));
for(int k =0,sz = vz[i].size();k<sz;++k){
int w = vz[i][k].X;
for(int j= W; j>=w;--j){
int v = vz[i][k].Y;
dp[i][j] = max(dp[i][j], max(dp[i-1][j-w]+v,
dp[i][j-w]+v));
}
}
}
else if(s==1){
for(int j= 0;j<=W;++j) dp[i][j] = dp[i-1][j];
for(int k = 0,sz = vz[i].size();k<sz;++k){
int w = vz[i][k].X;
for(int j=W;j>=w;--j){
int v = vz[i][k].Y;
dp[i][j] = max(dp[i][j],dp[i-1][j-w]+v);
}
}
}
else{
for(int j= 0;j<=W;++j) dp[i][j] = dp[i-1][j];
for(int k = 0,sz = vz[i].size();k<sz;++k){
int w = vz[i][k].X;
for(int j=W;j>=w;--j){
int v = vz[i][k].Y;
dp[i][j] = max(dp[i][j],max(dp[i][j-w]+v,
dp[i-1][j-w]+v));
}
}
}
}
printf("%d
",max(dp[n][W],-1));
}
return 0;
}