借此机会,整理一下背包中的某几类问题:
物品分组,每组至少选一个:
这个时候
写法1:看别人博客,这样写省去了某些麻烦问题
达不到的dp值为-INF
dp[i][j]=max(dp[i][j],max(dp[i][j-w[x]]+p[x],dp[i-1][j-w[x]]+p[x]));
也可设为-1
在状态转移时判断哪些状态是达不到的。
写法2:
if(dp[i][j-c[k]]!=-1)
dp[i][j]=max(dp[i][j],dp[i][j-c[k]]+w[k]);
if(dp[i-1][j-c[k]]!=-1)
dp[i][j]=max(dp[i][j],dp[i-1][j-c[k]]+w[k]);
这个时候必须注意两次转移的顺序,如果物品的消耗体积可以为0,两次转移的顺序必须严格按照上面,先用该组已有物品被选的状态转移,再用该组没有物品被选的状态转移。
初始化:每到一组,先赋值为 不可达,因为刚才始任何物品都没有选。
特点:必须用二维数组表示
物品分组,每一组至多选一个:
可以用二维表示,也可以用一维表示,
二维表示:
dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]);
初始化:通过上一组赋值即可,因为至多选一个,初始化时是一个都没选时的情形。
for(j=0;j<=sum;j++) //当前组初始化
dp[i][j]=dp[i-1][j];
int ret=dp2[v];
if(cost[ind]) dp2[v]=max( dp2[v] , dp2[v-cost[ind]]+val[ind] );//
else dp2[v]=max(dp2[v],ret+val[ind]);
一维表示必须对体积为0的物体单独处理,二维表示则自然省去了这些麻烦。
初始化:递推之前初始化。
普通01背包
可以用二维,可以用一维。
二维必须要通过拷贝上一组来初始化:
for(j=0;j<=sum;j++) //当前组初始化
dp[i][j]=dp[i-1][j];
dp[i][j]=max(dp[i][j],dp[i][j-w[x]]+p[x]);
一维在递推之前初始化:
此题不必对物品进行排序,虽然我排了。不用排序必须都用二维数组表示状态。
/**==========================================
* This is a solution for ACM/ICPC problem
*
* @source: hdu 3585 AreYouBusy
* @type: 混合背包,要求对背包有深入理解
* @author: wust_ysk
* @blog: http://blog.csdn.net/yskyskyer123
* @email: 2530094312@qq.com
*===========================================*/
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int INF =0x3f3f3f3f;
const int maxn=100 ;
const int maxV=100 ;
int n,V;
int cost[10000+10];
int val[10000+10];
int dp[maxn+4][maxV+4];
int dp2[maxn+4];
int things;
int st0,st1,st2;
struct A
{
int m,kind;
vector<int >ve;
bool operator<(const A y)const
{
return kind<y.kind;
}
}a[maxn+5];
void check()
{
st0=st1=st2=-1;
int i;
for( i=1;i<=n;i++)
{
if(a[i].kind==0&& st0==-1) st0=i;
else if(a[i].kind==1&& st1==-1) st1=i;
else if(a[i].kind==2&& st2==-1) st2=i;
}
memset(dp[0],-1, (V+1)*sizeof dp[0][1]);
dp[0][0]=0;
memset(dp2,-1,(V+1)*sizeof dp2[0]);
// dp2[0]=0;
}
void atleastone()
{
int st=st0;
if(st==-1) { dp2[0]=0; return;}//
int ed= ~st1 ?st1-1:st2-1;
if(st1==-1&&st2==-1) ed=n;//
for(int i=st;i<=ed;i++)
{
memset(dp[i],-1,(V+1)*sizeof dp[i][0]);
// memcpy(dp[i],dp[i-1],sizeof dp[i-1]);
vector<int > & vet=a[i].ve;
for(int j=0;j<vet.size();j++)
{
int ind=vet[j];
for(int v=V;v>=cost[ind];v--)
{
if( ~dp[i][v-cost[ind] ] ) dp[i][v]=max(dp[i][v],dp[i][v-cost[ind] ]+val[ind]);
if(~dp[i-1][v-cost[ind] ] ) dp[i][v]=max(dp[i][v],dp[i-1][v-cost[ind] ]+val[ind]);
}
}
}
for(int v=0;v<=V;v++)
{
dp2[v]=dp[ed][v];
}
}
void mostone()
{
int st=st1;
if(st==-1) return;
int ed=st2-1;
if(st2==-1) ed=n;
for(int i=st;i<=ed;i++)
{
vector<int > & vet=a[i].ve;
for(int v=V;v>=0;v--)
{
int ret=dp2[v];
for(int j=0;j<vet.size();j++)
{
int ind=vet[j];
if( v-cost[ind]<0||dp2[v-cost[ind]]==-1 ) continue;//
if(cost[ind]) dp2[v]=max( dp2[v] , dp2[v-cost[ind]]+val[ind] );//
else dp2[v]=max(dp2[v],ret+val[ind]);
}
}
}
}
void zeroone()
{
int st=st2;
if(st==-1) return;
int ed=n;
for(int i=st;i<=ed;i++)
{
vector<int > & vet=a[i].ve;
for(int j=0;j<vet.size();j++)
{
int ind=vet[j];
for(int v=V;v>=cost[ind];v--) if( ~dp2[v-cost[ind] ] )
{
dp2[v]=max(dp2[v],dp2[v-cost[ind] ]+val[ind]);
}
}
}
}
int main()
{
while(~scanf("%d%d",&n,&V))
{
things=0;
int tot=0;
for(int i=1;i<=n;i++)
{
int mini=INF;
scanf("%d%d",&a[i].m,&a[i].kind);
a[i].ve.clear();
for(int j=1;j<=a[i].m;j++)
{
scanf("%d%d",&cost[things],&val[things]);
a[i].ve.push_back(things);
if(!a[i].kind) mini=min(mini,cost[things]);
things++;
}
if(!a[i].kind) tot+=mini;
}
if(V<tot) {puts("-1");continue; }
if(!n) {puts("0");continue; }
sort(a+1,a+1+n);
check();
atleastone();
mostone();
zeroone();
int maxi=0;
for(int v=0;v<=V;v++)
{
maxi=max(dp2[v] ,maxi );
}
printf("%d\n",maxi);
}
return 0;
}