HDU 3535 AreYouBusy(组合背包)

题目链接:点击打开链接

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

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


题目大意:给你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 1e8

int 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

发布了37 篇原创文章 · 获赞 23 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/zsheng_/article/details/76818417
今日推荐