HDU_1712 ACboy needs your help(DP)

ACboy needs your help

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4538 Accepted Submission(s): 2428

Problem Description
ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?

Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.
Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].
N = 0 and M = 0 ends the input.

Output
For each data set, your program should output a line which contains the number of the max profit ACboy will gain.

Sample Input
2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0

Sample Output
3
4
6
题解
这是一道典型的分组背包问题,但是在把这个问题归类之前,先试着自己找思路解决这个问题。

一共有M天要分给N门课程,给我们了矩阵数组,让我们求出最大的复习效率。我们应该明白:复习时间的不同意味着得分的不同,但是对于每门课复习时间一定是确定的,前面课程复习时间的选择会对后面产生影响。我们很容易想到可以设置dp[i][j]表示前i门课一共复习j天的最大效率。所以:dp[i][j]=max(dp[i][j],dp[i-1][x]+A[i][j - x ])(0<=x<=j);

上面的思路其实是很清晰的,写出代码也很简单。下面我们看分组背包的定义,再对分组背包有更加深刻的理解。

分组背包:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

对照分组背包的定义,我们发现分组背包其实很好理解,分组之间互相影响,一个分组内只能选取一件,又归结为01背包问题,那么问题也就很好解决了。一维数组其实就可以。

使用一维数组的伪代码如下:
1 for 所有的组k
2 do for v V to 0
3 do for 所有的i属于组k
4 do f [v] = max{f[v],f[v-c[i]] + w[i]}
但是有两点需要注意:
1.“for v=V..0“这一层循环必须在“for 所有的i属于组k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。
2.类似01背包,第二层for循环一定是倒序。其实对倒序的理解一直有问题,我之前认为倒序的目的是01背包防止多次加入,但是从表达式我又认为是会改变滚动数组的值,其实两者的意思是一样的,一个是原理,一个是结果。所以这道题将分组内视为01背包就更好理解了。

二维dp代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <limits.h>
#define MAX_N 120
using namespace std;

//复习第i门课,已复习了j天的最大收益
int dp[MAX_N][MAX_N];
int N,M,result;
int A[MAX_N][MAX_N];
int main()
{
    while( scanf("%d%d",&N,&M) && N+M )
    {
        for( int i = 1; i <= N; i++ )
            for( int j = 1; j <= M; j++ )
                scanf("%d",&A[i][j]);
        memset(dp,0,sizeof(dp));
        result = 0;
        for( int j = 0; j <= M; j++ )
        {
            dp[1][j]=A[1][j];
            result=max(result,dp[1][j]);
        }
        for( int i = 2; i <= N; i++ )
        {
            for( int j = 0; j <= M; j++ )
            {
                for( int t = 0; t <= j; t++ )
                    dp[i][j]=max(dp[i][j],dp[i-1][t]+A[i][j-t]);
                result=max(result,dp[i][j]);
            }
        }
        printf("%d\n",result);
    }
    return 0;
}

一维DP代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <limits.h>
#define MAX_N 120
using namespace std;

//花费时间i所得到的最大收益
int dp[MAX_N];
int N,M,result;
int A[MAX_N][MAX_N];
int main()
{
    while( scanf("%d%d",&N,&M) && N+M )
    {
        for( int i = 1; i <= N; i++ )
            for( int j = 1; j <= M; j++ )
                scanf("%d",&A[i][j]);
        memset(dp,0,sizeof(dp));
        for( int i = 1; i <= N; i++ )
        {
            for( int j = M; j >= 0; j-- )
            {
                for( int t = 0; t <= j; t++ )
                    dp[j]=max(dp[j],dp[j-t]+A[i][t]);
            }
        }
        printf("%d\n",dp[M]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值