题意:
n个数中选k个数排成一圈,给一个L,圈中的连续的数的所有异或值中要组成的连续的[L,R]区间,求最大的R(1<=k<=n<=20,k<=6,1<=L<=100)
思路:
一看就是只能暴搜了啊,当时算了下时间复杂度感觉姿势不优美不可搜,现在写完发现确实需要比较优美的姿势才能搜过去。。。
一些剪枝和优化:
- 选k个数的时候2^n枚举,不要递归搜索
- 选了k个数之后先2^k枚举所有可能的异或值,判断一下能否组成比[L,ans]大的区间,不能的话不需要做下一步
- 由于选取k个数排成一圈能产生的连续异或值只有k*(k-1)+1种,所以每次判断就拿这么个范围的桶来做就可以了
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<iomanip>
#include<vector>
#include<set>
#include<map>
#include<queue>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
#define rep(i,k,n) for(int i=(k);i<=(n);i++)
#define rep0(i,n) for(int i=0;i<(n);i++)
#define red(i,k,n) for(int i=(k);i>=(n);i--)
#define sqr(x) ((x)*(x))
#define clr(x,y) memset((x),(y),sizeof(x))
#define max(x,y) (x>y?x:y)
#define swap(x,y) {int t=x;x=y;y=t;}
#define pb push_back
#define mod 1000000007
int n,k,L,ans;
int a[100],d[100];
bool xx[200];
void dfs2(int cnt)
{
if(cnt==k)
{
memset(xx+L,0,sizeof(xx[0])*(k*(k-1)+5));
int tt=0;
for(int len=1;len<k;len++)
{
tt^=d[0];xx[tt]=1;
for(int i=1;i<k;i++)
{
tt^=d[i]^d[(i-len+k)%k];
xx[tt]=1;
}
}
tt^=d[0];
xx[tt]=1;
int t=L;tt=0;
while(xx[t])tt=t,t++;
ans=max(ans,tt);
return;
}
for(int i=cnt;i<k;i++)
{
swap(d[cnt],d[i]);
dfs2(cnt+1);
swap(d[cnt],d[i]);
}
}
inline void check()
{
memset(xx+L,0,sizeof(xx[0])*(k*(k-1)+5));
int tot=1<<k;
for(int i=0;i<tot;i++)
{
int tmp=0;
for(int j=0;j<k;j++)if(i&(1<<j))
{
tmp^=d[j];
}
xx[tmp]=1;
}
int tt=0,t=L;
while(xx[t])tt=t,t++;
if(tt<=ans)return;
if(k==1)
{
ans=max(ans,d[0]);
return;
}
for(int i=1;i<k;i++)
{
swap(d[i],d[1]);
dfs2(2);
swap(d[i],d[1]);
}
}
int main()
{
while(~scanf("%d%d%d",&n,&k,&L))
{
rep0(i,n)scanf("%d",&a[i]);
ans=0;
int tot=1<<n;
for(int i=1;i<tot;i++)if(__builtin_popcount(i)==k)
{
int t=0;
for(int j=0;j<n;j++)if(i&(1<<j))
{
d[t++]=a[j];
}
check();
}
printf("%d\n",ans);
}
return 0;
}