分组

题目链接

  • 枚举每个数属于哪组,有\(\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;
}
posted @ 2024-07-31 10:33  D06  阅读(29)  评论(0编辑  收藏  举报