题目网址:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=34887
分析:题目的大概意思就是在剩余时间内,在给定了歌的时间的n首歌里,在所唱歌数最多的情况下,最后剩余的时间最少。这题的考点是比较明显的01背包,但是不容易想到的是我们不需要对歌的时间进行DP,只需要最后求出剩余时间最小就 可以了,所以我们可以对歌数进行DP,并储存所选歌曲的总时间,最后扫一遍就可以了
代码:
#include<cstdio>
#include<cstring>
int dp[9680], songtime[60];//由于剩余时间一定小于所有歌曲加上劲歌金曲的总时间,所以数组只要比50*180+678大一点即可
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int totaltime, i, j, n, t, T,ans;
scanf("%d", &T);
for (t = 1;t <= T;t++)
{
scanf("%d%d", &n,&totaltime);
for (i = 0;i < n;i++)
scanf("%d", &songtime[i]);
memset(dp, -1, sizeof dp);
dp[0] = 0;
//-1与0的意义为dp数组中当前阶段歌曲时间或当前阶段所选歌曲总时间会被标记,最终一定比不满足条件的数组位置的对应数大,达到了储存所选歌曲总时间的目的
for (i = 0;i < n;i++)
for (j = totaltime;j >= songtime[i];j--)
dp[j] = max(dp[j], dp[j - songtime[i]] + 1);
for (j = ans = totaltime - 1;j >= 0;j--)//由于一定得剩余时间给劲歌金曲,所以不能取刚好等于初始剩余时间的歌曲总数
if (dp[ans] < dp[j])
ans = j;//从后往前扫,保证在歌曲数最大的情况下,首先取到歌曲总时间较大的情况,所以只是<而没有=
printf("Case %d: %d %d\n", t,dp[ans]+1, ans + 678);
}
return 0;
}