混合三种背包问题。
定义: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][];
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=;i<=n;i++)
{
scanf("%d%d",&m,&s);
if(s == ) //至少选一项
{
for(j=;j<=T;j++)
dp[i][j] = -Mod;
for(j=;j<m;j++)
{
scanf("%d%d",&c,&g);
for(k=T;k>=;k--)
{
int t1 = dp[i][k]; //不选择此工作
int t2 = -Mod;
int t3 = -Mod;
if(k >= c)
{
t2 = dp[i-][k-c] + g; //第一次选此工作
t3 = dp[i][k-c] + g; //选择并且不是第一次选
}
dp[i][k] = Max(t1,t2,t3);
}
}
}
else if(s == ) //最多选一项
{
for(j=;j<=T;j++)
dp[i][j] = dp[i-][j]; //由上一堆推来
for(j=;j<m;j++)
{
scanf("%d%d",&c,&g);
for(k=T;k>=;k--)
{
int t1 = dp[i][k];
int t2 = -Mod;
if(k >= c)
t2 = dp[i-][k-c] + g;
dp[i][k] = max(t1,t2);
}
}
}
else if(s == ) //随便选
{
for(j=;j<=T;j++)
dp[i][j] = dp[i-][j];
for(j=;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],-));
}
return ;
}