HDU 3535 AreYouBusy(组合背包)

来源:互联网 发布:云计算的好处有哪些() 编辑:程序博客网 时间:2024/06/10 00:27

题目链接:点击打开链接

刚开始还以为是混合背包……

刚从一个难以处理背包上限的题走出来了,又进入一个考初始化的题,这是道好题啊好题!


题目大意:给你n组工作集合去选择和T分钟去完成它们,接下来有n组数据,每一组数据首先各给你一组m和s,m表示该组的工作数目,s表示你的选择方式(1.s=0表示至少选一个;2.s=1表示最多选一个;3.s=2表示自由选择),接着是m个工作,对应告诉你每个工作的工作时间ci和幸福指数gi,求:在T时间内能获得的最大幸福指数。


解题思路:如果最后存在一个最优解,即使互换工作集合的处理顺序,最后还是可以得到最优解,所以每个工作集合的工作选择是各自独立的,我们可以分开处理不同集合。

这道题用二维数组dp[ i ] [ j ] 来解,i 表示第几组,j 表示给定时间。每得到一组工作集合的数据就进行一次dp,所以dp[ i ][ j ]就是第i组的工作幸福指数。  三种情况分析如下:


1.第一类(s=0):至少在该组选择一个,即:不能不选,必须要选。在一开始就要将i=0时dp初始化为0,其他的初始化为-INF,这样就可以避免不选的情况了,并且,因为在题目中给出的ci,gi的取值可以为0,即可以有耗时和幸福度为0的选择,所以在初始化的时候要注意!。本类的dp数据可以由上一组的dp【i-1】来更新,也可以由当前组的前一个来更新后一个。因为该类中的dp一开始全部初始化为负无穷大,所以在选取时,要分是不是第一次拿,因此得到如下状态转换方程:

dp[ i ][ j ] = max( dp[ i-1 ][ j ] , dp[ i-1 ][ j - c ] + g , dp[ i ][ j ] + g );


2.第二类(s=1):最多选一项,即:选一个或者不选。该组的数据要从上一组更新而来,所以一开始要把上一组的数据复制过来。初始化工作:dp[ i ][ j ]=dp[ i-1 ][ j ];

状态转换方程:dp[ i ][ j ] = max ( dp[ i ][ j ],dp[ i ][ j - c ] + g );


3.第三类(s=2):自由选择,想选就选,不选就不选,普通的01背包问题。与第一类的区别就在于可以不选,所以初始化的时候不用讲究,直接将上一组的数据复制过来就行了。初始化前面一类中已给出,状态转移方程:dp[ i ][ j ]=max( dp[ i ][ j ],dp[ i ][ j - c] + g) ;



代码如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=100+5;#define INF 1e8int n;int t;int dp[maxn][maxn];int cost[maxn];int val[maxn];int main(){    while(~scanf("%d%d",&n,&t))    {        memset(dp,0,sizeof(dp));        for(int i=1; i<=n; i++)    //这里一定要在i=1开始循环,我就是从0开始,然后就一直错,半天找不出错啊难啊,哭死        {            int m,s;            scanf("%d%d",&m,&s);            for(int k=1; k<=m; k++)                scanf("%d%d",&cost[k],&val[k]);            if(s==0)//至少选1个的01背包问题            {                for(int j=0; j<=t; j++) dp[i][j]=-INF;                for(int k=1; k<=m; k++)                    for(int j=t; j>=cost[k]; j--) //逆序                    {                        dp[i][j] = max( dp[i][j], dp[i][j-cost[k]]+val[k] );   //第一次拿的数据要从上一组更新而来                        dp[i][j] = max( dp[i][j], dp[i-1][j-cost[k]]+val[k] );                    }            }            else if(s==1)//至多选1个的背包问题            {                for(int j=0; j<=t; j++) dp[i][j]=dp[i-1][j];                for(int k=1; k<=m; k++)                    for(int j=t; j>=cost[k]; j--) //j可以正序或逆序枚举                        dp[i][j] = max( dp[i][j], dp[i-1][j-cost[k]]+val[k] );            }            else if(s==2)//自由选择选的01背包问题            {                for(int j=0; j<=t; j++) dp[i][j]=dp[i-1][j];                for(int k=1; k<=m; k++)                    for(int j=t; j>=cost[k]; j--) //逆序枚举                        dp[i][j] = max( dp[i][j], dp[i][j-cost[k]]+val[k] );            }        }        int ans = max(dp[n][t],-1);        printf("%d\n",ans);    }    return 0;}




~ step by step

原创粉丝点击