当前位置: 首页 > 工具软件 > backpack > 使用案例 >

2022“杭电杯”中国大学生算法设计超级联赛(1)1003 Backpack个人题解

杜霍英
2023-12-01

Backpack

题目描述

有n件物品,他们各自都有体积vi和价值wi,给你一个体积为m的背包,求是否能从这n件物品中取出若干件,使得它们的体积之和恰好为m,所有物品的异或和最大,最大值是多少

分析

这道题是在01背包的基础上的扩展,其中n,m满足 1≤n,m<210,vi,wi 满足 1≤vi,wi<210 ,我们可以用f[i] [j] [k] 表示在前i个物品中选出若干件,异或和为j,体积为k的方案是否存在,那么,我们可以将状态划分为:
①取第i件物品,f[i] [j] [k] = f[i - 1] [j ⊕ w] [k - v]
为什么是j ⊕ w呢?
因为如果取了第i件物品,那么异或和就变成了j ⊕ w ⊕ w = j,体积变成了k - v + v = k,所以这种情况下 f[i] [j] [k] = f[i - 1] [j ⊕ w] [k - v]
②没有取第i件物品,f[i] [j] [k] = f[i - 1] [j] [k]
所以最终状态转移方程为:f[i] [j] [k] = f[i - 1] [j ⊕ w] [k - v] | f[i - 1] [j] [k]

这样的话就是三维,由于题目中给的所有数值都是小于1024的,我们可以用bitset优化成二维 ,用f[i] [j]表示异或值为i,体积为j的方案是否存在

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1050;
int n,m;
bitset<N> f[N],g[N];

void solve()
{
    for(int i = 0;i < 1024;i ++){
        f[i].reset();
        g[i].reset();
    }
    f[0][0] = 1;//初始化,异或和为0,体积为0的方案是存在的
    cin >> n >> m;
    for(int i = 1;i <= n;i ++){
        int v,w;
        cin >> v >> w;
        for(int j = 0;j < 1024;j ++){
            g[j] = f[j];//用g[]来记录上一次的状态
            g[j] <<= v;//为什么是左移v位呢?这一步表示当异或和为j时,
            //取第j件物品,将g[j]左移v位,最终如果存在左移m位的方案时,
            //就说明存在使得所有物品体积和为m的方案,即如果存在f[i][m] == 1时,
            //就说明存在异或和为i,取得的所有物品体积和为m的方案
        }
        for(int j = 0;j < 1024;j ++){
            f[j] |= g[j ^ w];//取当前这件物品,就是g[j ^ w],不取这件物品就是f[j]
        }
    }
    int ans = -1;
    for(int i = 0;i < 1024;i ++){
        if(f[i][m]) ans = i;
    }
    cout << ans << '\n';
}

int main()
{
    int t;
    cin >> t;
    while (t --){
        solve();
    }
    return 0;
}

大家如果有不懂的地方欢迎在评论区留言~
创作不易,留下你的赞叭~

 类似资料: