HDU_3535_AreYouBusy(多种背包组合)

AreYouBusy

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3887    Accepted Submission(s): 1536


Problem Description
Happy New Term!
As having become a junior, xiaoA recognizes that there is not much time for her to AC problems, because there are some other things for her to do, which makes her nearly mad.
What's more, her boss tells her that for some sets of duties, she must choose at least one job to do, but for some sets of things, she can only choose at most one to do, which is meaningless to the boss. And for others, she can do of her will. We just define the things that she can choose as "jobs". A job takes time , and gives xiaoA some points of happiness (which means that she is always willing to do the jobs).So can you choose the best sets of them to give her the maximum points of happiness and also to be a good junior(which means that she should follow the boss's advice)?
 

Input
There are several test cases, each test case begins with two integers n and T (0<=n,T<=100) , n sets of jobs for you to choose and T minutes for her to do them. Follows are n sets of description, each of which starts with two integers m and s (0<m<=100), there are m jobs in this set , and the set type is s, (0 stands for the sets that should choose at least 1 job to do, 1 for the sets that should choose at most 1 , and 2 for the one you can choose freely).then m pairs of integers ci,gi follows (0<=ci,gi<=100), means the ith job cost ci minutes to finish and gi points of happiness can be gained by finishing it. One job can be done only once.
 

Output
One line for each test case contains the maximum points of happiness we can choose from all jobs .if she can’t finish what her boss want, just output -1 .
 

Sample Input
  
  
3 3 2 1 2 5 3 8 2 0 1 0 2 1 3 2 4 3 2 1 1 1 3 4 2 1 2 5 3 8 2 0 1 1 2 8 3 2 4 4 2 1 1 1 1 1 1 0 2 1 5 3 2 0 1 0 2 1 2 0 2 2 1 1 2 0 3 2 2 1 2 1 1 5 2 8 3 2 3 8 4 9 5 10
 

Sample Output
  
  
5 13 -1 -1
 

Author
hphp
 

Source
 

Recommend
zhouzeyong


一个多种背包dp混合在一起的题目

每一个组别是一种背包类型


题意

有n个组的工作,有t的时间去做

对于组有三种类型

2随意,拿或者不拿,选其中多少都可以

1最多选一个

0至少选一个

每组每件事有花费的时间和收益

取尽量高的收益


解题思路

先考虑由于0组别的存在,导致有可能最后所给的时间不足以完成要求

而0组别也是这个题目的难点

平时我们处理这种至少要拿一个的问题可以把空间全部刷成-1 dp[0]=0就可以了

但是这个题目由于要在原来的基础上做 所以需要另外开一个dp数组

1的话至多选一个就是分组背包,但是直接用分组背包写这个题目是不行的

 原因等下讲

2的条件最低

对组内所有的成员做一遍01背包就可以了


好了现在来讲下这个题目的坑点。

这个题目中存在体积为0(时间为0)的工作


想想这会对上面的哪些造成影响?

组别为2的不会受影响。

组别为1的,由于0的存在,用分组背包就不能保证最多选一个了。

    因此必须也开一个单独的dp重新利用过去的状态一个一个推

最别为0的,会受到影响吗?这就看关系式怎么写了

   由于状态中-1这种非法状态的的存在,所以如果用先判断原先数组,求一个dp[i],再用现在的数组求一个dp[i]的话

   当体积为0时,0的权值会被算两遍,因此这里比较的顺序特别的重要。

递推关系详见代码。

先上一个有问题的代码。

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

const int MV=105;
int dp[MV];
int dp2[MV];
const int MG=105;
const int M=105;
int ng,vm;//组数
struct Group
{
    int m,s;//组中元素个数,种类
    int vi[M],wi[M];
    //占空间 价值
}group[MG];
void _zop(int vi,int wi)//01背包子过程
{
    for(int i=vm;i>=vi;i--)
        if(dp[i-vi]!=-1)
           dp[i]=max(dp[i],dp[i-vi]+wi);
}
void _gp(int g)//分组背包子过程
{
    for(int i=vm;i>=0;i--)
        for(int k=1;k<=group[g].m;k++)//这样写不行!
        {
            //cout<<"!!i k "<<i<<" "<<k<<" "<<dp[i]<<endl;
            //cout<<"!!vi wi "<<group[g].vi[k]<<" "<<group[g].wi[k]<<endl;
            if(group[g].vi[k]<=i&&dp[i-group[g].vi[k]]!=-1)
                dp[i]=max(dp[i],dp[i-group[g].vi[k]]+group[g].wi[k]);
            //cout<<"!!!i k "<<i<<" "<<k<<" "<<dp[i]<<endl;
        }

}
void show(int i)
{
    cout<<i<<endl;
    for(int i=0;i<=vm;i++)
        cout<<dp[i]<<" ";
    cout<<endl;
}
void gp()
{
    for(int i=1;i<=ng;i++)
    {
        if(group[i].s==2)//
        {
            for(int k=1;k<=group[i].m;k++)
                _zop(group[i].vi[k],group[i].wi[k]);
            //show(2);
        }
        else if(group[i].s==1)//
        {
            _gp(i);
            //show(1);
        }
        else
        {
            memcpy(dp2,dp,sizeof(dp));
            memset(dp,-1,sizeof(dp));
            for(int k=1;k<=group[i].m;k++)
            {
                for(int j=vm;j>=group[i].vi[k];j--)
                {
                    if(dp[j-group[i].vi[k]]!=-1)
                        dp[j]=max(dp[j],dp[j-group[i].vi[k]]+group[i].wi[k]);
                    if(dp2[j-group[i].vi[k]]!=-1)
                        dp[j]=max(dp[j],dp2[j-group[i].vi[k]]+group[i].wi[k]);
                }
            }
            //show(0);
        }
    }
    //return 1;
}

int main()
{
    //freopen("1.in","r",stdin);
    int n,t;
    while(scanf("%d%d",&n,&t)!=EOF)
    {
        ng=n;vm=t;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&group[i].m,&group[i].s);
            for(int j=1;j<=group[i].m;j++)
                scanf("%d%d",&group[i].vi[j],&group[i].wi[j]);
        }
        gp();
        int ans=-1;
        for(int i=0;i<=t;i++)
            ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
    return 0;
}

AC代码

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

const int MV=105;
int dp[MV];
int dp2[MV];
const int MG=105;
const int M=105;
int ng,vm;//组数
struct Group
{
    int m,s;//组中元素个数,种类
    int vi[M],wi[M];
    //占空间 价值
}group[MG];
void _zop(int vi,int wi)//01背包子过程
{
    for(int i=vm;i>=vi;i--)
        if(dp[i-vi]!=-1)
           dp[i]=max(dp[i],dp[i-vi]+wi);
}
void _gp(int vi,int wi)//分组背包子过程
{
    for(int i=vi;i<=vm;i++)
        if(dp[i-vi]!=-1)
        dp[i]=max(dp[i],dp2[i-vi]+wi);
}
void gp()
{
    for(int i=1;i<=ng;i++)
    {
        if(group[i].s==2)//
        {
            for(int k=1;k<=group[i].m;k++)
                _zop(group[i].vi[k],group[i].wi[k]);
        }
        else if(group[i].s==1)//
        {
            memcpy(dp2,dp,sizeof(dp));
            for(int k=1;k<=group[i].m;k++)
                _gp(group[i].vi[k],group[i].wi[k]);
        }
        else
        {
            memcpy(dp2,dp,sizeof(dp));
            memset(dp,-1,sizeof(dp));
            for(int k=1;k<=group[i].m;k++)
            {
                for(int j=vm;j>=group[i].vi[k];j--)
                {
                    if(dp[j-group[i].vi[k]]!=-1)
                        dp[j]=max(dp[j],dp[j-group[i].vi[k]]+group[i].wi[k]);
                    if(dp2[j-group[i].vi[k]]!=-1)
                        dp[j]=max(dp[j],dp2[j-group[i].vi[k]]+group[i].wi[k]);
                }
            }
        }
    }
    //return 1;
}

int main()
{
    //freopen("1.in","r",stdin);
    int n,t;
    while(scanf("%d%d",&n,&t)!=EOF)
    {
        ng=n;vm=t;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&group[i].m,&group[i].s);
            for(int j=1;j<=group[i].m;j++)
                scanf("%d%d",&group[i].vi[j],&group[i].wi[j]);
        }
        gp();
        int ans=-1;
        for(int i=0;i<=t;i++)
            ans=max(ans,dp[i]);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值