HDU_1712 ACboy needs your help(DP)

来源:互联网 发布:知乎怎么匿名提问 编辑:程序博客网 时间:2024/05/20 14:42

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 120using 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 120using 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 0