先给出关于01背包恰好装满情况的的一篇文章,写的很好 传送门
题目链接
题意:
KTV里面有n首歌曲你可以选择,每首歌曲的时长都给出了. 对于每首歌曲,你最多只能唱1遍. 现在给你一个时间限制t (t<=10^9) , 问你在最多t-1秒的时间内可以唱多少首歌曲num , 且最长唱歌时间是多少time (time必须<=t-1) ? 最终输出num+1 和 time+678 即可.
注意: 你需要优先让歌曲数目最大的情况下,再去选择总时长最长的.
思路:
别看给的时间是1e9,因为每一首歌不超过180,而且最多就50首歌,所以其实最多时间也就9000,也就不需要考虑时间问题。
这其实是一个恰好装满的问题,因为问的是唱歌的时间,不然就是直接 给的剩余时间 -1 + 678就行,因为是以让歌曲数目最多为第一目的,所以我们要找到 t - 1内的最多的歌曲数目,并要求这个曲目数量时,这些歌必须装满相对应包(因为是问的唱的歌的总时间,所以要求对应的包必须装满)
所以我们要求出恰好装满时的最大歌曲数和这个歌曲的总时间
我们规定 dp[j] 是 剩余时间为 j 时 恰好能装满 j 时的歌曲数量,然后我们倒着进行枚举 从t - 1开始
找到歌曲数量最大的有效状态,并记录此时的容量,因为是有效状态,所以,这个包里一定是装满的,也就是说
这个包的容量就等于枚举到的这个数字
由于是枚举的有效状态,所以找到最大歌曲数量之后 + 1输出,并直接把容量也跟着 + 678输出即可
code
#include <bits/stdc++.h>
#define ss system("pause");
using namespace std;
const int maxn = 5e1 + 5;
const int inf = 0x3f3f3f3f;
const int maxc = maxn * 180 + 678;
int dp[maxc],v[maxn];
int main()
{
int t,k = 0;
cin>>t;
while(t--){
int n,c;
k++;
cin>>n>>c;
for(int i = 1; i <= n; i++) cin>>v[i];
memset(dp,-inf,sizeof dp);
dp[0] = 0;
for(int i = 1; i <= n; i++){
for(int j = c - 1; j >= v[i]; j--){
if(dp[j] < dp[j - v[i]] + 1){
dp[j] = dp[j - v[i]] + 1;
}
if(dp[j] < 0) dp[j] = -inf;
}
}
int maxx = 0,ans = 0;
for(int i = c - 1; i >= 0; i--){
if(dp[i] > maxx){
maxx = dp[i];
ans = i;
}
}
printf("Case %d: %d %d\n",k,maxx + 1,ans + 678);
}
//ss
return 0;
}