HDU3535
题解:分组背包问题好题目。
定义dp[i][j]为考虑到第i组,背包容量为j时的最大价值
- s == 0,至少选一个。那么dp[i]初始化为-inf。dp[i][k]要从dp[i][k-weight]+val和dp[i-1][k-weight]+val转移过来。所以 dp[i][k] = max(dp[i][k],max(dp[i][k-weight]+val,dp[i-1][k-weight]+val))。仔细想想,因为初始化为-inf,所以第一次从dp[i-1]转移过来一定选中了第i组的一件物品。
- s == 1,至多选一个。这就变成了普通的01背包问题。从上一个状态转移过来。
- s == 2,随便选。这类似于完全背包问题。
代码:
#include <bits/stdc++.h>
using namespace std;
int const inf = 0x3f3f3f3f;
int const N = 100 + 10;
int n,m,s,T,weight,val;
int dp[N][N]; //dp[i][j]表示考虑到第i组,容量为j的最大价值
int main(){
while(~scanf("%d%d",&n,&T)){ //剩余T的时间
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){ //共有n组
scanf("%d%d",&m,&s); //每组有m个任务,类型为s;
if(s == 0){ //但是至少得有一个工作,带限制的完全背包问题
for(int j=0;j<=T;j++) dp[i][j] = -inf; //初始化负无穷,从后面的状态转移过来,保证选一个
for(int j=1;j<=m;j++){
scanf("%d%d",&weight,&val);
for(int k=T;k>=weight;k--) //逆序,每一组可选择多个,但是每一个之多选一个
dp[i][k] = max(dp[i][k],max(dp[i][k-weight]+val,dp[i-1][k-weight]+val));
}
}
else if(s == 1){ //普通01背包问题,最多选一个,从上一个状态转移
for(int j=0;j<=T;j++) dp[i][j] = dp[i-1][j];
for(int j=1;j<=m;j++){
scanf("%d%d",&weight,&val);
for(int k=T;k>=weight;k--) //可正或逆
dp[i][k] = max(dp[i][k],dp[i-1][k-weight]+val);
}
}
else{ //01背包背包,无限制选择,从上一个状态转移,
for(int j=0;j<=T;j++) dp[i][j] = dp[i-1][j];
for(int j=1;j<=m;j++){
scanf("%d%d",&weight,&val);
for(int k=T;k>=weight;k--) //逆序,总共有m件物品,对于每一件物品,最多选一个
dp[i][k] = max(dp[i][k],dp[i][k-weight]+val); //在目前的状态上转移
}
}
}
printf("%d\n",dp[n][T] < 0 ? -1:dp[n][T]);
}
return 0;
}