题意:
给出n件物品,每件物品有一个容量 t i t_{i} ti与价值 v i v_{i} vi,现有一个容量为 m m m的背包,选出一个组合恰好塞满背包,并且使所有选择物品的异或和最大,输出这个异或和,若不存在这样的组合,输出-1
分析:
对这个01背包,可以用存在性 d p dp dp,设 d p [ i ] [ j ] dp[i][j] dp[i][j]为容量为 i i i,已选取的物品的异或和为 j j j是否可能存在,转移也非常简单 d p [ i ] [ j ] = d p [ i − t [ k ] ] [ j x o r v [ k ] ] dp[i][j]=dp[i-t[k]][j\ xor\ v[k]] dp[i][j]=dp[i−t[k]][j xor v[k]],但是这样枚举会超时,可以利用 b i t s e t bitset bitset节省时间,一个 b i t s e t bitset bitset可以看作一个 b o o l bool bool数组,那么可以把 b o o l d p [ 1100 ] [ 1100 ] bool\ dp[1100][1100] bool dp[1100][1100]换成 b i t s e t < 1100 > d p [ 1100 ] bitset<1100>dp[1100] bitset<1100>dp[1100], b i t s e t bitset bitset节省时间在利用左右移和整体赋值来达到节省时间。在这里,如果把原本 d p dp dp数组的前后意义交换 d p [ i ] [ j ] dp[i][j] dp[i][j]表示为异或和为 i i i时,容量组合为 j j j的组合是否存在,那么 d p [ i ] [ j ] = d p [ i x o r v [ k ] ] [ j − t [ k ] ] dp[i][j]=dp[i\ xor\ v[k]][j-t[k]] dp[i][j]=dp[i xor v[k]][j−t[k]],可以理解为,当前 i i i这一行的所有数都由 i x o r v [ k ] i\ xor\ v[k] i xor v[k]那一行对应位置的往前 t [ k ] t[k] t[k]位转移过来,普通的 b o o l bool bool数组不能左移, b i t s e t bitset bitset可以,所以这里设 b i t s e t < 1100 > d p [ 1100 ] , d p [ i ] bitset<1100>dp[1100],dp[i] bitset<1100>dp[1100],dp[i]意为异或和为 i i i的所有容量可能情况,那么 d p [ i ] ∣ = d p [ i x o r v [ k ] ] < < t [ k ] dp[i]|=dp[i\ xor\ v[k]]<<t[k] dp[i]∣=dp[i xor v[k]]<<t[k],右式的 d p dp dp是计算完上一个物品的结果,因此在计算每一个物品时,需要保存前一个物品的结果。
总结一下:
b i t s e t bitset bitset节省时间就在于整个数组左移右移后的整体赋值,发现本来的 d p dp dp数组恰好某一行完全由另一行转移来,并且是每一个位置都是从那一列的对应位置的前 k k k个位置转移来,也就是前一个符合了 b i t s e t bitset bitset整体赋值,后一个符合了 b i t s e t bitset bitset左移节省时间的特点,这样的情况是适合用 b i t s e t bitset bitset的
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read()
{
int ret=0,base=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') base=-1;
ch=getchar();
}
while(isdigit(ch))
{
ret=(ret<<3)+(ret<<1)+ch-48;
ch=getchar();
}
return ret*base;
}
int n,m,t[1100],v[1100];
bitset<1100>dp[1100],last[1100];
void work()
{
n=read();m=read();
for(int i=0;i<1024;i++) dp[i].reset();
for(int i=1;i<=n;i++)
{
t[i]=read();
v[i]=read();
}
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1023;j>=0;j--) last[j]=dp[j];
for(int j=1023;j>=0;j--) dp[j]|=last[j^v[i]]<<t[i];
}
int ans=-1;
for(int i=0;i<1024;i++)
{
if(dp[i][m]) ans=i;
}
cout<<ans<<endl;
}
int main()
{
int t=read();
while(t--) work();
return 0;
}