AreYouBusy

AreYouBusy

题意:

给你n个工作集合,给你T的时间去做它们。给你m和s,说明这个工作集合有m件事可以做,它们是s类的工作集合(s=0,1,2,s=0说明这m件事中最少得做一件,s=1说明这m件事中最多只能做一件,s=2说明这m件事你可以做也可以不做)。再给你ci和gi代表你做这件事要用ci的时间,能获得gi的快乐值。求在T的时间内你能获得的最大快乐值。

题解:

我们设dp[i][j]表示处理完前i组工作集,所用时间小于j的快乐值
dp[i]表示的是第i组的结果,每组相对独立,
所以对于每一组情况,我们就可以将dp看作是一维数组,只考虑第二维j,考虑第一维仅为需要继承状态时
我们分析三种集合:

  1. 至少选一项。在开始时dp初始化为负无穷,这样是为了保证不会出现都不不选的情况,负无穷相当于这个情况不合法即未选
    转移方程:
dp[i][j]=max(dp[i][j],max(dp[i][j-w[x]]+p[x],dp[i-1][j-w[x]]+p[x]));

dp[i][j]表示不选这个工作
dp[i][j-w[x]]+p[x]:表示选择当前工作,且不是第一次取(为什么这个能保证不是第一次取?因为我们一开始将dp[i][…]赋值为负无穷,如果dp[i][j-w[x]]没被选过就还是负无穷,则整个式子取最大就跟他无关,如果被选过,则dp[i][j-w[x]]为正数)
dp[i-1][j-w[x]]+p[x]:第一次在本组中选物品,由于dp初始化为负无穷,所以状态的转移只能从上一组的结果中得知,这样可以保证得到全局最优解
这样,当有一组无法完成时,最后的答案就是负无穷

  1. 最多选一项,要么不选,要么就是第一次选
    为了保证全局最优解,dp[i][j]继承上一组的状态
    第一次选的话,和第一种集合的情况一样
dp[i][j]=max(dp[i][j],dp[i-1][j-w[x]]+p[x]);
  1. 任意选就是01背包
    为了保证全局最优解,dp[i][j]继承上一组的状态
dp[i][j]=max(dp[i][j],dp[i][j-w[x]]+p[x]);

这里的dp是被压缩了一维的,这里的dp[][j]中的j等于01背包的一维数组dp[j],
dp[i][]中的i只是表示第i组的情况

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int INF=0x3f3f3f3f;

int n,m,sum;
int w[110],p[110];
int dp[110][110];

int main(){

    //freopen("input.txt","r",stdin);

    while(~scanf("%d%d",&n,&sum)){
        memset(dp,0,sizeof(dp));
        int i,j,k,g;
        for(i=1;i<=n;i++)
		{
            scanf("%d%d",&m,&g);
            for(k=1;k<=m;k++)
                scanf("%d%d",&w[k],&p[k]);
            if(g==0){//至少选一个 
                for(j=0;j<=sum;j++) //当前组初始化
                    dp[i][j]=-INF;
                for(k=1;k<=m;k++)
                    for(j=sum;j>=w[k];j--)
                        dp[i][j]=max(dp[i][j],max(dp[i][j-w[k]]+p[k],dp[i-1][j-w[k]]+p[k]));
            }else if(g==1){//最多选一个 
                for(j=0;j<=sum;j++) //当前组初始化
                    dp[i][j]=dp[i-1][j];
                for(k=1;k<=m;k++)
                    for(j=sum;j>=w[k];j--)
                        dp[i][j]=max(dp[i][j],dp[i-1][j-w[k]]+p[k]); 
                        //dp[i-1][j-w[k]]
                        //j-w[k]<j
            }else if(g==2){//自由选择 
                for(j=0;j<=sum;j++) //当前组初始化
                    dp[i][j]=dp[i-1][j];
                for(k=1;k<=m;k++)
                    for(j=sum;j>=w[k];j--)
                        dp[i][j]=max(dp[i][j],dp[i][j-w[k]]+p[k]);
            }
        }
        dp[n][sum]=max(dp[n][sum],-1);  //没有完成任务的值都为负的,做输出调整,输出-1
        printf("%d\n",dp[n][sum]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值