题目大意
输入n,t(n首歌,剩余t秒)和每首歌的长度。
输出可以唱的最多的总歌曲数目
以及
唱歌的时间长度。
样例
input
2
3 100
60 70 80
3 100
30 69 70
output
Case 1: 2 758
Case 2: 3 777
解释
第一个样例中,两首歌:80 + 678 = 758
第二个样例中 ,三首歌:30 + 69 + 678 = 777
思路
就是一个简单的01背包,不过有两个状态需要记录,而且这两个状态之间是有关系的。
在满足唱的曲目最多的条件下,时间最长。
所以状态转移方程是这样的:
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= t; j++){
dp[i][j][0] = dp[i - 1][j][0];
dp[i][j][1] = dp[i - 1][j][1];
if (j >= w[i])
{
if (dp[i][j][0] < dp[i - 1][j - w[i]][0] + 1)//首先考虑唱的曲目最多
{
dp[i][j][0] = dp[i - 1][j - w[i]][0] + 1;
dp[i][j][1] = dp[i - 1][j - w[i]][1] + w[i];
}
else if (dp[i][j][0] == dp[i - 1][j - w[i]][0] + 1){//曲目相等的情况下才考虑时间
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - w[i]][1] + w[i]);
}
}
}
}
代码
#include <stdio.h>
#include <cstring>
#define max(a,b) (a>b)? a:b
#define min(a,b) (a<b)? a:b
#define MAXN 55
#define MAXT 10000
int n, t;
int w[MAXN];
int dp[MAXN][MAXT][2];//在第i层,时间为t的时候,能唱的最多的歌曲数目
int main()
{
int CaseNum,kase;
scanf("%d", &CaseNum);
for (int kase = 1; kase <= CaseNum; kase++)
{
scanf("%d %d", &n, &t);
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= t; j++){
dp[i][j][0] = dp[i - 1][j][0];
dp[i][j][1] = dp[i - 1][j][1];
if (j >= w[i])
{
if (dp[i][j][0] < dp[i - 1][j - w[i]][0] + 1)
{
dp[i][j][0] = dp[i - 1][j - w[i]][0] + 1;
dp[i][j][1] = dp[i - 1][j - w[i]][1] + w[i];
}
else if (dp[i][j][0] == dp[i - 1][j - w[i]][0] + 1){
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - w[i]][1] + w[i]);
}
}
}
}
printf("Case %d: %d %d\n", kase, dp[n][t - 1][0] + 1, dp[n][t - 1][1]+678);
}
}
/*
Sample Input
100
3 100
60 70 80
3 100
30 69 70
13 747
78 90 50 31 89 137 78 52 87 96 128 163 92
16 780
47 70 119 33 67 74 62 120 162 175 7 111 92 100 18 42
44 1311
151 91 13 21 23 164 105 127 126 47 145 52 140 95 57 112 171 168 113 127 83 78 17 175 130 130 146 59 68 49 138 150 179 98 80 9 142 38 128 151 60 9 61 81
Sample Output
Case 1: 2 758
Case 2: 3 777
Case 3: 11 1421
Case 4: 13 1455
Case 5: 24 1987
*/
Hit
这两个状态转移的过程一定是有关系的,容易犯的错误就是分开考虑