hdu 3535 AreYouBusy 经典混合背包

借此机会,整理一下背包中的某几类问题:


物品分组,每组至少选一个:

这个时候


写法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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值