先上题目:
Time Limit: 3000/1000 MS
(Java/Others) Memory Limit: 65535/65535 K
(Java/Others)
Total Submission(s): 4722 Accepted
Submission(s): 1497
题意:给你n部电影,l分钟,每部电影有时长和观看价值,要你从中选不多不少m部电影出来,,保证在l分钟以内观看价值是最大的。
这是一条二维01背包,基本做法只需在一维的基础上再加一维,不过这里还有一个限制条件,那就是不多不少选m部,所以需要保证最终选到的电影恰好有m部,而不是最多有m部。
状态转移方程: j>=c[i] && k>=1 && dp[j-c[i]][k-1]>=0(意思是中间选了k-1部片子)时 dp[j][k] = max{dp[j][k] , dp[j-c[i]][k-1]+v[i]}
具体过程看代码
上代码:
#include <cstdio> #include <cstring> #define MAX 1001 #define max(x,y) (x > y ? x : y) using namespace std; int v[100],c[100],dp[MAX][101]; int n,m,l; int main() { int t; //freopen("data.txt","r",stdin); scanf("%d",&t); while(t--){ scanf("%d %d %d",&n,&m,&l); for(int i=0;i<n;i++){ scanf("%d %d",&c[i],&v[i]); } /** * 初始化是关键,只有j==0的时候全部赋为0,代表一开始的最大值为0,赋为-1,是为了 * 保证最终选取m部片子,不多不少,如果中间少选了片子的话,是得到-1而已 */ for(int i=0;i<=l;i++){ for(int j=0;j<=m;j++){ dp[i][j]= (j==0 ? 0 : -1); } } /** * 在别人的代码中发现了新的优化技巧,那就是因为当j<c[i]的时候永远是跳过的,所以j的范围改成l~c[i] * 可以提高效率,也就是说将范围压缩在边界~当前物品的对应代价之间 */ for(int i=0;i<n;i++){ for(int j=l;j>=c[i];j--){ for(int k=m;k>=1;k--){ if(dp[j-c[i]][k-1]>=0) dp[j][k]=max(dp[j][k] , dp[j-c[i]][k-1]+v[i]); //printf("%d ",dp[j][k]); } //printf(" "); } //printf("\n"); } /** * 如果有最大值,一定会出现在dp[l][m]中,因为dp[l][]的的含义就是前l个物品中的最大值 */ if(dp[l][m]>=0) printf("%d\n",dp[l][m]); else printf("0\n"); } return 0; }
原文:http://www.cnblogs.com/sineatos/p/3530029.html