2024牛客寒假算法基础集训营1 H01背包,但是bit

原题链接:H-01背包,但是bit_2024牛客寒假算法基础集训营1 (nowcoder.com)

题目大意: 一共n件物品,每个物品有二个属性,分别是重量和价值。给定一个m为最大重量,求总重量不超过m的最大价值,每个物品的重量和计算方式为按位或运算。

n的范围是[1,1e5],m的范围是[0,1e8],重量和价值的范围是[0,1e8]。

思路:因为重量的计算方式是按位或运算,可以想到将最大重量m用二进制来表示,m的小于1e8,在int的范围内,所以可以直接枚举31位二进制,例如m的二进制为100011,如果一个物品重量二进制为1000000,那么如果选这个物品的那么一定会超重。那我们可以枚举m的每一个二进制数,从左往右枚举。

1. 如果还没有枚举到1,那么可选物品的这些位数也必须为0。

2. 如果第一次枚举到了1,那么有二种情况,第一种是选择可选物品的最左边的1在m枚举的1右边。例如m的二进制为100011,其中一个物品的二进制为001111,那么这个物品最左边的1的位置是4,m枚举到的1位置是5,那么就肯定可以选,因为将这些物品重量的二进制全部按位或最大值也只有011111,肯定小于m,这样可以得到一个总价值。第二种情况是可选物品重量的二进制数1和m的二进制数1在同一个位置,那么枚举物品的时候就会被下一个m二进制数1限制。例如m的二进制数为100011,其中一共物品重量的二进制数为100010不包括在第一种情况,但是却是可以选择的,其中另一个物品重量的二进制数为100101,就不可以选择。第二种情况的总价值暂时无法计算的到,为了求出这种情况,用一个数来表示m被枚举二进制的前缀。当枚举到了m二进制数为100011的第二个1的时候,那么现在这个1的第一种情况求出来的总价值就是m二进制数第一个1的第二种情况的一部分,另一部分是这个1的第二种情况。

3. 枚举到m二进制数1之后枚举到0,使用之前保存的前缀来枚举每个物品。

4. 枚举到了m二进制数的最后一位,如果是0的话,m二进制数的最后一位1,就不存在第二种情况,如果是1的话,就存在第二种情况,为了处理第二种情况,可以让m来枚举每一个为物品。不判断直接让m也进行一次比较也是可以的。

不懂的,直接看代码吧。

#pragma GCC optimize(2)
#include
#define endl '\n' 
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair pii;
const int N=1e6+10;
ll v[N],w[N];
int main()
{
    ios::sync_with_stdio(NULL);
    cin.tie(0),cout.tie(0);
    ll t;cin>>t;
    while(t--)
    {
        ll n,m;cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            cin>>v[i]>>w[i];
        }
        ll max1=0,pre=0;
        for(int i=31;i>=0;i--)//枚举m的二进制数的每一位
        {
            ll k=pre;//让k的前缀与m的前缀保持一致,例如m的为10100,k就是10000
            if((m>>i)&1)
            {
                k+=(1<

你可能感兴趣的:(算法,数据结构)