01背包变式 要求恰好装满的01背包

先给出关于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;
}



猜你喜欢

转载自blog.csdn.net/CUCUC1/article/details/109775409