题目链接
- 枚举每个数属于哪组,有\(\frac {4^{18}}{24}\)种情况,我们无法承受这样的时间复杂度
- 但是我们可以用\(2^n\)的时间枚举每个数属于左边两组还是右边两组
- 由于分组是无序的,我们强制规定a1属于左边两组,时间复杂度降至\(2^{n-1}\)
- 在每个大组中,用01背包统计合法状态,这一过程可以在搜索中顺便完成
- 对于每种分组情况,我们分类讨论,将合法状态按w排序后用双指针维护前缀最大值
- 我们提前给下标按w排序,这样我们就不需要每次统计答案时都对当前的合法状态按w排序了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[20],w[1505],q[1505];
int sum[2],l[2];
int ans;
bool f[2][20][1505];
int g[2][1505];
bool cmp(int a,int b)
{
return w[a]<w[b];
}
void dfs(int n1)
{
if(n1==n+1)
{
g[0][0]=g[1][0]=0;
for(int i=0;i<2;i++)
{
for(int j=0;j<(1<<m);j++)
{
if(f[i][l[i]][q[j]]&&(w[sum[i]^q[j]]<w[q[j]]||w[sum[i]^q[j]]==w[q[j]]&&(sum[i]^q[j])<=q[j]))
{
g[i][++g[i][0]]=q[j];
}
}
}
int k=0,maxn=0;
for(int i=1;i<=g[0][0];i++)
{
int j=sum[0]^g[0][i];
while(k+1<=g[1][0]&&w[g[1][k+1]]<=w[g[0][i]])
{
k++;
maxn=max(maxn,w[g[1][k]^sum[1]]);
}
if(k!=0)
{
ans=min(ans,w[g[0][i]]-min(maxn,w[j]));
}
}
k=0;maxn=0;
for(int i=1;i<=g[1][0];i++)
{
int j=sum[1]^g[1][i];
while(k+1<=g[0][0]&&w[g[0][k+1]]<=w[g[1][i]])
{
k++;
maxn=max(maxn,w[g[0][k]^sum[0]]);
}
if(k!=0)
{
ans=min(ans,w[g[1][i]]-min(maxn,w[j]));
}
}
}
else
{
for(int i=0;i<2;i++)
{
sum[i]^=a[n1];
l[i]++;
for(int k=0;k<(1<<m);k++)
{
f[i][l[i]][k]=f[i][l[i]-1][k]|f[i][l[i]-1][k^a[n1]];
}
dfs(n1+1);
for(int k=0;k<(1<<m);k++)
{
f[i][l[i]][k]=false;
}
l[i]--;
sum[i]^=a[n1];
}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=0;i<(1<<m);i++)
{
scanf("%d",&w[i]);
q[i]=i;
}
sort(q,q+(1<<m),cmp);
ans=INT_MAX;
sum[0]=a[1];sum[1]=0;
l[0]=1;l[1]=0;
f[0][0][0]=f[1][0][0]=true;
f[0][1][0]=f[0][1][a[1]]=true;
dfs(2);
f[0][1][a[1]]=false;
cout<<ans<<endl;
}
return 0;
}