混合三种背包问题。
定义:dp[i][k]表示体积为k的时候,在前i堆里拿到的最大价值。
第一类,至少选一项,dp初值全赋为负无穷,这样才能保证不会出现都不选的情况。
dp[i][k] =
max(dp[i][k],max(dp[i-1][k-c]+g,dp[i][k-c]))
其中:
dp[i][k]是不选当前项
dp[i-1][k-c]+g是表示第一次选这组的物品
dp[i][k-c]+g表示选择当前物品,并且不是第一次选。
第二类最多选一个,一旦选则是第一次选
dp[i][k] =
max(dp[i][k],dp[i-1][k-c]+g);
注意全局最优解,需复制上一组解到这一组。
第三类,无限制,则跑一个01背包
dp[i][k] = max(dp[i][k],dp[i][k-c]+g)
注意仍需复制上一组解。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 10007 int dp[N][107]; int c,g; int Max(int k1,int k2,int k3) { return max(max(k1,k2),k3); } int main() { int n,T,i,j,k; int m,s,set; while(scanf("%d%d",&n,&T)!=EOF) { for(i=1;i<=n;i++) { scanf("%d%d",&m,&s); if(s == 0) //至少选一项 { for(j=0;j<=T;j++) dp[i][j] = -Mod; for(j=0;j<m;j++) { scanf("%d%d",&c,&g); for(k=T;k>=0;k--) { int t1 = dp[i][k]; //不选择此工作 int t2 = -Mod; int t3 = -Mod; if(k >= c) { t2 = dp[i-1][k-c] + g; //第一次选此工作 t3 = dp[i][k-c] + g; //选择并且不是第一次选 } dp[i][k] = Max(t1,t2,t3); } } } else if(s == 1) //最多选一项 { for(j=0;j<=T;j++) dp[i][j] = dp[i-1][j]; //由上一堆推来 for(j=0;j<m;j++) { scanf("%d%d",&c,&g); for(k=T;k>=0;k--) { int t1 = dp[i][k]; int t2 = -Mod; if(k >= c) t2 = dp[i-1][k-c] + g; dp[i][k] = max(t1,t2); } } } else if(s == 2) //随便选 { for(j=0;j<=T;j++) dp[i][j] = dp[i-1][j]; for(j=0;j<m;j++) { scanf("%d%d",&c,&g); for(k=T;k>=c;k--) { dp[i][k] = max(dp[i][k],dp[i][k-c]+g); } } } } printf("%d\n",max(dp[n][T],-1)); } return 0; }
UESTC 424 AreYouBusy,布布扣,bubuko.com
原文地址:http://www.cnblogs.com/whatbeg/p/3762760.html